/*************************************************************************
Purpose: All GAIA functions are written here in their entirety.
         The Makefile compiles all this into functions.o.
         This allows any program that is linked to functions.o
         to use the functions originally developed for gaia.cpp.
**************************************************************************
Copyright 2010 Bryan Killett

This file is part of GAIA.

GAIA is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GAIA is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GAIA. If not, see <http://www.gnu.org/licenses/>.
*************************************************************************/
//Header file "definitions.hpp" declares many functions, constants, datatypes.
//In order to use the functions declared in definitions.hpp, must link
//with object code that includes functions.cpp.
#include "definitions.hpp"

void write_options(string &options_file,
                   input_s &input,
                   grid_options_s &region_options,
                   ancillary_s &ancillary,
                   grid_s &grid,
                   model_options_s &model_options,
                   analysis_options_s &analysis_options,
                   plot_options_s &plot_options,
                   insitu_s &insitu,
                   double parameter[],
                   long long parameter_type[],
                   long long num_parameters,
                   long long manual_list[],
                   long long num_manual_list,
                   double custom_lats[],
                   double custom_lons[],
                   long long num_custom_lats){
  /**********************************************************************
  Purpose: This function writes various user-set options to a file.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          string options_file - Name of file with options from gaia.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  FILE *new_fp;

  ////////////////////////////////////////////////////////////////////////////
  //Initialization and over-ride section. Nothing here is PC specific.
  ////////////////////////////////////////////////////////////////////////////

  //Specified lat/lon isn't used in some types, so it needs to be zeroed.
  if(grid.options.type == 3){
    grid.options.specified_lon = 0.0;
    cout<<"Grid type "<<grid.options.type<<" doesn't use specified_lon, so it's being zeroed."<<endl;
  }
  else if(grid.options.type != 11){
    grid.options.specified_lat = 0.0;
    grid.options.specified_lon = 0.0;
    cout<<"Grid type "<<grid.options.type<<" doesn't use specified_lat/lon, so they're being zeroed."<<endl;
  }

  //Grid type 11 and region type 3 need cap_size -1.
  if(grid.options.type == 11 and grid.options.cap_size != -1){
    cout<<"Grid type "<<grid.options.type<<" set grid cap_size to -1."<<endl;
    grid.options.cap_size = -1;
  }
  if(region_options.type == 3 and region_options.cap_size != -1){
    cout<<"Region type "<<region_options.type<<" set region cap_size to -1."<<endl;
    region_options.cap_size = -1;
  }

  //Region 3 needs to be after grid type 11, which should auto-choose this type.
  if(region_options.type == 3){//This region snaps to spec pt, so cap must be -1.
    //Just in case cap_size wasn't -1, reset so it doesn't affect cache loading.
    region_options.cap_size = -1;
  }

  //If using accel code, revert to a 1 degree cap bc GRACE's
  //orbit gives a 1 degree cap 32k data points in 5 years, which is enough
  //for acceleration maps.
  if(analysis_options.type == 1 or analysis_options.type == 2 or analysis_options.type == 3 or analysis_options.type == 4){
    grid.options.cap_size = deg2km;//1 degree in km.
    analysis_options.upward_continuation_verification_choice = 0;
    region_options.type = 0;
    //input.options.split_input = 0;
    cout<<"Acceleration maps need 1.0 degree caps, so automatic cap sizing is being disabled.  Also, upcontverif and region code disabled, but split_input is temporarily being allowed to run."<<endl;
  }

  //If using equal grid grids, revert to a 1 degree cap bc GRACE's
  //orbit gives a 1 degree cap 32k data points in 5 years, and it can't
  //be made any smaller without having ANY data points.
  if(grid.options.type==1){
    grid.options.cap_size = deg2km;//1 degree in km.
    cout<<"Equal grid grids (usually) require 1.0 degree caps, so automatic cap sizing is being disabled."<<endl;
  }

  if(analysis_options.disk_radius != grid.options.lat_spacing/2.0) cout<<"!!WARNING!! disk_radius = "<<analysis_options.disk_radius<<" KM WHEN IT SHOULD BE "<<grid.options.lat_spacing/2.0<<" KM IN ORDER TO BE HALF THE GRID SPACING!!"<<endl;

  //Some analysis types require regions, but can't use parallel code b/c
  //find_and_load_parallel_output() can't deal with it. (Although write
  //and read misc output functions have been turned off for type 10 ONLY.)
  if(analysis_options.type == 5 or analysis_options.type == 6 or analysis_options.type == 7 or analysis_options.type == 10 or analysis_options.type == 11 or analysis_options.type == 12 or analysis_options.type == 13 or analysis_options.type == 14 or analysis_options.type == 15){
    region_options.parallel = 0;
    analysis_options.print_immediate_results=0;
    cout<<"Disabling parallel code because an analysis type with output data incompatible with find_and_load_parallel_output() was requested. Also disabled print_immediate_results because that causes a crash too!"<<endl;
  }

  //If loading old output or skipping analysis, region code WON'T work right.
  if((analysis_options.type == 0 or analysis_options.type == -1) and region_options.type != 0){
    region_options.type = 0;
    cout<<"Disabling region code because analysis type just loads previously computed output files."<<endl;
  }

  //If plotting ancillary data, region code MIGHT not work right.
  if(analysis_options.type >= 20001 and region_options.type != 0){
    region_options.type = 0;
    cout<<"Disabling region code because analysis type just plots elevation."<<endl;
  }

  //If I'm just making simulations, no need for region code.
  /*if(analysis_options.type >= 10001 and region_options.type != 0){
    region_options.type = 0;
    cout<<"Disabling region code because analysis type just makes simulations."<<endl;
  }//*/

  //If model is used, region code screws up use of the model struct because it hasn't
  //been copied to the regional grid.
  if(analysis_options.type >= 1000 and analysis_options.type <= 2000 and region_options.type != 0){
    region_options.type = 0;
    cout<<"Disabling region code because analysis type just plots model."<<endl;
  }//*/

  //Older input files do not contain orbit data.  If loading one of these
  //files (identifiable because the filename doesn't contain "_and_orbit",
  //set this variable to 1.  Otherwise it's 0.
  //If this filename contains "region_", it needs to be read.
  if(((int)input.options.filename.find("_and_orbit"))!=(int)string::npos) input.options.no_orbit_data = 0;
  else input.options.no_orbit_data = 1;

  //Time parallel code should auto-select sequential region code.
  if(analysis_options.time_parallel > 0 and region_options.type != 0){
    region_options.parallel = 0;
    cout<<"Temporally parallel code has auto-selected sequential region code."<<endl;
  }

  //If I'm using zone of influence code, it's strange to have the overlap
  //distance be less than the zone radius, so warn if that's the case but
  //otherwise don't do anything.
  if(region_options.overlap_distance < analysis_options.zone_radius){
    cout<<"!!!WARNING!!! Selected overlap distance "<<region_options.overlap_distance<<" is less than the radius of the zone of influence ("<<analysis_options.zone_radius<<")!!!"<<endl;
    cout<<"FOR THE MOMENT, NO ACTION IS BEING TAKEN BECAUSE ZONE OF INFLUENCE MAY NOT EVEN BE INVOLVED IN THIS INVERSION."<<endl;
  }

  ////////////////////////////////////////////////////////////////////////////
  //Adjust parameters so constants and trends have 0.0 as their parameters.
  ////////////////////////////////////////////////////////////////////////////

  //Polynomial params are set to 0.0 to help least squares cache system.
  //New parameter types should be added here.
  for(i=0;i<num_parameters;i++){
    if(parameter_type[i] < 100)
      parameter[i]=0.0;//Polynomials are always 0.
    else if(parameter_type[i] == 101)
      parameter[i]=parameter[i];//To avoid "not recognized" warning.
    else if(parameter_type[i] == 10101 or parameter_type[i] == 10102)
      parameter[i]=parameter[i];//These are arbitrary cosine,sine periods.
    else if(parameter_type[i] >= 10000 and parameter_type[i] < 20000)
      parameter[i]=0.0;//Arbitrary time series parameters are otherwise 0.
    else cout<<"Parameter type "<<parameter_type[i]<<" was not recognized."<<endl;
  }

  ///////////////////////////////////////////////////////////////////////////
  //Write options to disk.
  ///////////////////////////////////////////////////////////////////////////

  new_fp = fopen(options_file.c_str(),"w");
  fprintf(new_fp,"Save overlap points:\n%d\n",analysis_options.save_overlap_points);
  fprintf(new_fp,"Save regional input data:\n%d\n",analysis_options.save_regional_input_data);
  fprintf(new_fp,"Print immediate results:\n%d\n",analysis_options.print_immediate_results);
  fprintf(new_fp,"In situ noload:\n%d\n",insitu.noload);
  fprintf(new_fp,"Grid noload:\n%d\n",grid.options.noload);
  fprintf(new_fp,"Region noload:\n%d\n",region_options.noload);
  fprintf(new_fp,"Least squares noload:\n%d\n",analysis_options.least_squares_noload);
  fprintf(new_fp,"Insitu comparison type:\n%d\n",insitu.comparison_type);
  fprintf(new_fp,"Grid type:\n%d\n",grid.options.type);
  fprintf(new_fp,"Grid latlon:\n%d\n",grid.options.latlon);
  fprintf(new_fp,"Grid latlon lat multiplier:\n%lld\n",grid.options.latlon_lat_multiplier);
  fprintf(new_fp,"Grid latlon lon multiplier:\n%lld\n",grid.options.latlon_lon_multiplier);
  fprintf(new_fp,"Grid cap size:\n");
  fprintf(new_fp,DOUBLE_FORMAT,grid.options.cap_size);
  fprintf(new_fp,"Grid lat spacing:\n");
  fprintf(new_fp,DOUBLE_FORMAT,grid.options.lat_spacing);
  fprintf(new_fp,"Grid lon spacing:\n");
  fprintf(new_fp,DOUBLE_FORMAT,grid.options.lon_spacing);
  fprintf(new_fp,"Grid area type:\n%d\n",grid.options.area_type);
  fprintf(new_fp,"Disk radius for mascons and cm2kg:\n");
  fprintf(new_fp,DOUBLE_FORMAT,analysis_options.disk_radius);
  fprintf(new_fp,"Grid Boundary - minimum lat:\n");
  fprintf(new_fp,DOUBLE_FORMAT,grid.options.boundaries[0]);
  fprintf(new_fp,"Grid Boundary - maximum lat:\n");
  fprintf(new_fp,DOUBLE_FORMAT,grid.options.boundaries[1]);
  fprintf(new_fp,"Grid Boundary - minimum lon:\n");
  fprintf(new_fp,DOUBLE_FORMAT,grid.options.boundaries[2]);
  fprintf(new_fp,"Grid Boundary - maximum lon:\n");
  fprintf(new_fp,DOUBLE_FORMAT,grid.options.boundaries[3]);
  fprintf(new_fp,"Supporting grid type:\n%d\n",grid.options.support_type);
  fprintf(new_fp,"Supporting grid lat multiplier:\n%d\n",grid.options.support_lat_multiplier);
  fprintf(new_fp,"Supporting grid lon multiplier:\n%d\n",grid.options.support_lon_multiplier);
  fprintf(new_fp,"Region type:\n%d\n",region_options.type);
  fprintf(new_fp,"Region cap size:\n");
  fprintf(new_fp,DOUBLE_FORMAT,region_options.cap_size);
  fprintf(new_fp,"Region lat spacing:\n");
  fprintf(new_fp,DOUBLE_FORMAT,region_options.lat_spacing);
  fprintf(new_fp,"Region lon spacing:\n");
  fprintf(new_fp,DOUBLE_FORMAT,region_options.lon_spacing);
  fprintf(new_fp,"Region overlap distance:\n");
  fprintf(new_fp,DOUBLE_FORMAT,region_options.overlap_distance);
  fprintf(new_fp,"Regions in parallel:\n%d\n",region_options.parallel);
  fprintf(new_fp,"Time parallel:\n%d\n",analysis_options.time_parallel);
  fprintf(new_fp,"Number of CPUs:\n%d\n",analysis_options.numprocesses);
  fprintf(new_fp,"Zone of influence choice:\n%d\n", analysis_options.zone_of_influence_choice);
  fprintf(new_fp,"No plots:\n%d\n", plot_options.noplots);
  fprintf(new_fp,"Zone of influence radius:\n");
  fprintf(new_fp,DOUBLE_FORMAT,analysis_options.zone_radius);
  fprintf(new_fp,"Region analysis list choice:\n%d\n",region_options.analysis_list_choice);
  fprintf(new_fp,"Grid specified lat:\n");
  fprintf(new_fp,DOUBLE_FORMAT,grid.options.specified_lat);
  fprintf(new_fp,"Grid specified lon:\n");
  fprintf(new_fp,DOUBLE_FORMAT,grid.options.specified_lon);
  fprintf(new_fp,"Region specified lat:\n");
  fprintf(new_fp,DOUBLE_FORMAT,region_options.specified_lat);
  fprintf(new_fp,"Region specified lon:\n");
  fprintf(new_fp,DOUBLE_FORMAT,region_options.specified_lon);
  fprintf(new_fp,"Region analysis list cache only:\n%d\n",region_options.analysis_list_cache_only);
  //If a manual region list was specified, record it (only used if analysis
  //list choice says so).
  if(num_manual_list > 0){
    fprintf(new_fp,"Number manually specified regions:\n%lld\n",num_manual_list);
    for(i=0;i<num_manual_list;i++) fprintf(new_fp,"%lld\n",manual_list[i]);
  }
  //If no model is requested, don't bother loading the model.
  fprintf(new_fp,"Model options type:\n%d\n",model_options.type);
  fprintf(new_fp,"Model noload:\n%d\n",model_options.noload);
  fprintf(new_fp,"Model interpolation halfwidth:\n%20.9f\n",model_options.halfwidth);
  fprintf(new_fp,"Model interpolation averaging radius:\n%20.9f\n",model_options.averaging_radius);
  fprintf(new_fp,"Analysis type:\n%d\n",analysis_options.type);
  fprintf(new_fp,"Analysis lambda:\n%20.9f\n",analysis_options.lambda);
  fprintf(new_fp,"Regularization parameter 1:\n%20.9f\n",analysis_options.regparam1);
  fprintf(new_fp,"Regularization parameter 2:\n%20.9f\n",analysis_options.regparam2);
  fprintf(new_fp,"Regularization parameter integer 1:\n%d\n",analysis_options.regparamint1);
  fprintf(new_fp,"Convergence threshold for refined inversions:\n");
  fprintf(new_fp,DOUBLE_FORMAT,analysis_options.convergence);

  fprintf(new_fp,"Number of parameters:\n%lld\n",num_parameters);//Only used for loading.
  for(i=0;i<num_parameters;i++){
    fprintf(new_fp,DOUBLE_FORMAT,parameter[i]);
    fprintf(new_fp,"%lld\n",parameter_type[i]);
  }
  fprintf(new_fp,"H matrix choice:\n%d\n",analysis_options.h_matrix_choice);
  fprintf(new_fp,"Maximum N for disk mascons:\n%lld\n",analysis_options.nmax);
  fprintf(new_fp,"Output folder:\n%s\n",plot_options.outputfolder.c_str());
  fprintf(new_fp,"input folder:\n%s\n",input.options.basefolder.c_str());
  fprintf(new_fp,"input filename:\n%s\n",input.options.filename.c_str());
  fprintf(new_fp,"input_options.no_orbit_data:\n%d\n", input.options.no_orbit_data);
  fprintf(new_fp,"input_options.split_input:\n%d\n", input.options.split_input);
  fprintf(new_fp,"input_options.chunk:\n%lld\n", input.options.chunk);
  fprintf(new_fp,"input_options.buffer:\n%lld\n", input.options.buffer);
  fprintf(new_fp,"upward_continuation_verification_choice:\n%d\n",analysis_options.upward_continuation_verification_choice);
  fprintf(new_fp,"skip_upward_continuation_during_verification:\n%d\n",analysis_options.skip_upward_continuation_during_verification);
  fprintf(new_fp,"folder_for_skipping:\n%s\n",analysis_options.folder_for_skipping.c_str());

  //Only write these values to the options.txt file if a custom grid is requested.
  if(grid.options.type==0){
    fprintf(new_fp,"Number of custom grid points:\n%lld\n",num_custom_lats);
    for(i=0;i<num_custom_lats;i++){
      fprintf(new_fp,DOUBLE_FORMAT,custom_lats[i]);
      fprintf(new_fp,DOUBLE_FORMAT,custom_lons[i]);
    }
  }

  fprintf(new_fp,"Topography type:\n%d\n",ancillary.topo.type);
  fprintf(new_fp,"In situ cache folder:\n%s\n",insitu.cachefolder.c_str());
  fprintf(new_fp,"Tide gauge folder:\n%s\n",insitu.tg.basefolder.c_str());
  fprintf(new_fp,"Grid cache folder:\n%s\n",grid.options.cachefolder.c_str());
  fprintf(new_fp,"Model cache folder:\n%s\n",model_options.cachefolder.c_str());
  fprintf(new_fp,"Region cache folder:\n%s\n",region_options.cachefolder.c_str());
  fprintf(new_fp,"Least squares cache folder:\n%s\n", analysis_options.least_squares_cachefolder.c_str());
  fprintf(new_fp,"input cache folder:\n%s\n",input.options.cachefolder.c_str());
  fprintf(new_fp,"arbitrary cache folder:\n%s\n",input.arbitrary.cachefolder.c_str());

  fclose(new_fp);
}

void load_options(string &input_file,
                  input_s &input,
                  grid_options_s &region_options,
                  ancillary_s &ancillary,
                  grid_s &grid,
                  model_options_s &model_options,
                  results_s &results,
                  plot_options_s &plot_options,
                  insitu_s &insitu,
                  int verbose){
  /**********************************************************************
  Purpose: This function loads various user-set options from a file
          created by write_options().
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          string input_file - Name of file with options from gaia.
          int verbose - 1/0 turns on/off messages.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  char s[max_length];
  int pos;//Used to remove \n at end of lines.
  int i,j;//counters
  double temp;//temp variable.
  long long templong;

  //For some reason this variable is necessary when loading input.cachefolder
  string temp_string;

  char* f_return;//Used to stop fgets return value warning.

  //////////////////////////////////////////////////////////////////////
  //Initialize some variables (so old options files work correctly).
  //////////////////////////////////////////////////////////////////////

  //Unless the options file says not to load the input, assume that
  //it's supposed to be loaded.
  input.options.noload = 0;

  //Default to old region list.
  region_options.analysis_list_choice = 0;
  region_options.analysis_list_cache_only = 0;

  //Other more recent variables should be initialized to "old"
  //values so that if I ever decide to load old options.txt files, they
  //aren't set to weird values because the new vars aren't in old files.
  results.options.upward_continuation_verification_choice = 0;
  input.options.split_input = 0;

  //If not specified, temporal parallelization not used.
  results.options.time_parallel = 0;

  //Zone of influence is disabled if not specified in options file.
  results.options.zone_of_influence_choice = 0;
  results.options.zone_radius = 0.0;

  //This variable is intended to be reset in the program right after the
  //call to load_options().  If it isn't, it should default to a map.
  results.options.output_choice = 1;

  //Used to control cm2kg and mascon down/up continuation code.
  //New, so I'm defaulting it to 100- half the spacing between 200km gr pts.
  results.options.disk_radius = 100;

  //Assume plots are requested unless specified otherwise.
  plot_options.noplots=0;

  //Old options.txt files don't have specified_lat/lon, so zero them.
  grid.options.specified_lat=0.0;
  grid.options.specified_lon=0.0;
  region_options.specified_lat=0.0;
  region_options.specified_lon=0.0;

  //Used for debug, should be init'd to 0 so it doesn't run by default.
  model_options.record_nearby_values=0;

  //Defaults to "unknown."
  results.options.current_phase_format = 0;

  //Defaults to "Sean Swenson's topography file."
  ancillary.topo.type = 1;
  grid.options.topo_type = ancillary.topo.type;//Copy for grid cache substring.

  //Defaults to not writing analysis results (also in overlap grid points!) to disk.
  results.options.save_overlap_points=0;

  //Definitely doesn't save all the input data found in each region.
  results.options.save_regional_input_data=0;

  //Immediately prints results at first grid point unless disabled.
  results.options.print_immediate_results=1;

  grid.options.latlon = 0;//0 if unstructured grid, >1 for equal-grid latlon grid.
  grid.options.current_latlon = 0;//Set to >0 to have define_grid make a latlon grid.
  grid.options.latlon_lat_multiplier = 1;//Defaults to 1x.
  grid.options.latlon_lon_multiplier = grid.options.latlon_lat_multiplier;

  //If != 0, any verification choice calling for upward continuation will
  //skip that process and instead load the subfolder "folder_for_skipping".
  results.options.skip_upward_continuation_during_verification = 0;

  //////////////////////////////////////////////////////////////////////
  //End of initialization section.
  //////////////////////////////////////////////////////////////////////

  //Open options file for loading.
  FILE *in_fp;
  in_fp = fopen(input_file.c_str(),"r");

  //Warn if file doesn't open correctly.
  if(in_fp == NULL) cout << "The options file, " << input_file
                   << ", failed to open."<<endl;
  i=0;
  //Read the file line by line.
  while(fgets(s,max_length,in_fp)!=NULL){
    i+=2;//Just read the title AND value.  Not valid after unrecognized line!
    //Read odd numbered lines to determine what the next line is going
    //to be, then load it into the correct variable.
    if(!strcmp(s,"In situ noload:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &insitu.noload);
    }
    else if(!strcmp(s,"Insitu comparison type:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &insitu.comparison_type);
    }
    else if(!strcmp(s,"Save overlap points:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.save_overlap_points);
    }
    else if(!strcmp(s,"Save regional input data:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.save_regional_input_data);
    }
    else if(!strcmp(s,"Print immediate results:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.print_immediate_results);
    }
    else if(!strcmp(s,"Grid noload:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &grid.options.noload);
    }
    else if(!strcmp(s,"Model noload:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &model_options.noload);
    }
    else if(!strcmp(s,"Region noload:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &region_options.noload);
    }
    else if(!strcmp(s,"Least squares noload:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.least_squares_noload);
    }
    else if(!strcmp(s,"input noload:\n")){//refers to cache
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &input.options.nocache);
    }
    else if(!strcmp(s,"input_options.no_orbit_data:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &input.options.no_orbit_data);
    }
    else if(!strcmp(s,"input_options.split_input:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &input.options.split_input);
    }
    else if(!strcmp(s,"input_options.chunk:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &input.options.chunk);
    }
    else if(!strcmp(s,"input_options.buffer:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &input.options.buffer);
    }
    else if(!strcmp(s,"Model options type:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &model_options.type);
    }
    else if(!strcmp(s,"Model interpolation halfwidth:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &model_options.halfwidth);
    }
    else if(!strcmp(s,"Model interpolation averaging radius:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &model_options.averaging_radius);
    }
    else if(!strcmp(s,"Grid type:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &grid.options.type);
    }
    else if(!strcmp(s,"Supporting grid type:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &grid.options.support_type);
    }
    else if(!strcmp(s,"Supporting grid lat multiplier:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &grid.options.support_lat_multiplier);
    }
    else if(!strcmp(s,"Supporting grid lon multiplier:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &grid.options.support_lon_multiplier);
    }
    else if(!strcmp(s,"Grid latlon:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &grid.options.latlon);
    }
    else if(!strcmp(s,"Grid latlon lat multiplier:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &grid.options.latlon_lat_multiplier);
    }
    else if(!strcmp(s,"Grid latlon lon multiplier:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &grid.options.latlon_lon_multiplier);
    }
    else if(!strcmp(s,"Grid cap size:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &grid.options.cap_size);
    }
    else if(!strcmp(s,"Grid lat spacing:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &grid.options.lat_spacing);
    }
    else if(!strcmp(s,"Grid lon spacing:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &grid.options.lon_spacing);
    }
    else if(!strcmp(s,"Grid area type:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &grid.options.area_type);
    }
    else if(!strcmp(s,"Disk radius for mascons and cm2kg:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &results.options.disk_radius);
    }
    else if(!strcmp(s,"Grid Boundary - minimum lat:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &grid.options.boundaries[0]);
    }
    else if(!strcmp(s,"Grid Boundary - maximum lat:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &grid.options.boundaries[1]);
    }
    else if(!strcmp(s,"Grid Boundary - minimum lon:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &grid.options.boundaries[2]);
    }
    else if(!strcmp(s,"Grid Boundary - maximum lon:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &grid.options.boundaries[3]);
    }
    else if(!strcmp(s,"Grid Land mask:\n")){//Obsolete
      f_return=fgets(s,max_length,in_fp);
      //sscanf(s,"%d", &templong);
    }
    else if(!strcmp(s,"Grid specified lat:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &grid.options.specified_lat);
    }
    else if(!strcmp(s,"Grid specified lon:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &grid.options.specified_lon);
    }
    else if(!strcmp(s,"Region specified lat:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &region_options.specified_lat);
    }
    else if(!strcmp(s,"Region specified lon:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &region_options.specified_lon);
    }
    else if(!strcmp(s,"Region type:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &region_options.type);
    }
    else if(!strcmp(s,"Regions in parallel:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &region_options.parallel);
    }
    else if(!strcmp(s,"Region cap size:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &region_options.cap_size);
    }
    else if(!strcmp(s,"Region lat spacing:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &region_options.lat_spacing);
    }
    else if(!strcmp(s,"Region overlap distance:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &region_options.overlap_distance);
    }
    else if(!strcmp(s,"Region lon spacing:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &region_options.lon_spacing);
    }
    else if(!strcmp(s,"Region Land mask:\n")){//Obsolete
      f_return=fgets(s,max_length,in_fp);
      //sscanf(s,"%d", &templong);
    }
    else if(!strcmp(s,"Region analysis list choice:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &region_options.analysis_list_choice);
    }
    else if(!strcmp(s,"Region analysis list cache only:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &region_options.analysis_list_cache_only);
    }
    else if(!strcmp(s,"Number manually specified regions:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &j);
      region_options.manual_list.clear();
      if(j>0){
        region_options.manual_list.resize(j,-1);
        i+=j;//Reading more values this time.
        //Right after this line always follows manual region list.
        for(j=0;j<(long long)region_options.manual_list.size();j++){
          f_return=fgets(s,max_length,in_fp);
          sscanf(s,"%d", &pos);
          //-1 because they're entered as numbers not indices.
          region_options.manual_list.at(j) = pos-1;
        }
      }
    }
    else if(!strcmp(s,"Time parallel:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.time_parallel);
    }
    else if(!strcmp(s,"Number of CPUs:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.numprocesses);
    }
    else if(!strcmp(s,"Zone of influence choice:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.zone_of_influence_choice);
    }
    else if(!strcmp(s,"No plots:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &plot_options.noplots);
    }
    else if(!strcmp(s,"Zone of influence radius:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &results.options.zone_radius);
    }
    else if(!strcmp(s,"Analysis type:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.type);
    }
    else if(!strcmp(s,"Analysis lambda:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &results.options.lambda);
    }
    else if(!strcmp(s,"Regularization parameter 1:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &results.options.regparam1);
    }
    else if(!strcmp(s,"Regularization parameter 2:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &results.options.regparam2);
    }
    else if(!strcmp(s,"Regularization parameter integer 1:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.regparamint1);
    }
    else if(!strcmp(s,"Convergence threshold for refined inversions:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &results.options.convergence);
    }
    else if(!strcmp(s,"upward_continuation_verification_choice:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.upward_continuation_verification_choice);
    }
    else if(!strcmp(s,"skip_upward_continuation_during_verification:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.skip_upward_continuation_during_verification);
    }
    else if(!strcmp(s,"folder_for_skipping:\n")){
      f_return=fgets(s,max_length,in_fp);
      results.options.folder_for_skipping=s;
      //Remove the \n at the end of the line.
      if((pos=results.options.folder_for_skipping.find("\n"))!=(int)string::npos){
        results.options.folder_for_skipping.swap(results.options.folder_for_skipping.erase(pos,2));
      }
    }
    else if(!strcmp(s,"Number of parameters:\n")){
      long long numparameters;
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &numparameters);
      i+=numparameters*2;//Reading more values this time.
      results.options.parameter.clear();
      results.options.parameter_type.clear();
      results.options.parameter.resize(numparameters,0.0);
      results.options.parameter_type.resize(numparameters,-1);
      //Right after this line always follows parameter values and types.
      for(j=0;j<numparameters;j++){
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf", &temp);
        results.options.parameter.at(j) = temp;
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%d", &pos);
        results.options.parameter_type.at(j) = pos;
      }
    }
    else if(!strcmp(s,"Number of custom grid points:\n")){
      if(grid.options.type!=0) cout<<"WARNING!!!! options.txt file contains custom grid points, but grid.options.type = "<<grid.options.type<<endl;
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &templong);
      i+=templong*2;//Reading more values this time, lat/lon for all gr pts.
      grid.lat.clear(); grid.lon.clear();
      grid.lat.resize(templong,0.0);
      grid.lon.resize(templong,0.0);
      //Right after this line always follows custom grid lats/lons.
      for(j=0;j<(long long)grid.lat.size();j++){
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf", &temp);
        grid.lat.at(j) = temp;
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf", &temp);
        grid.lon.at(j) = temp;
        cout<<"SHOULD FIX BOUNDARY CHECK ON CUSTOM GR PTS BY USING ACTUAL BOUNDARY ARRAY, WHICH REQUIRES THAT CUSTOM GR PTS BE IN FILE AFTER BOUNDARIES."<<endl;
        if(fabs(grid.lat.at(j)) > 90 or grid.lon.at(j) >= 360.0 or grid.lon.at(j) < 0.0) cout<<"!!WARNING!! Grid point at (lat,lon) = ("<<grid.lat.at(j)<<", "<<grid.lon.at(j)<<") is out of bounds!"<<endl;
      }
    }
    else if(!strcmp(s,"H matrix choice:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &results.options.h_matrix_choice);
    }
    else if(!strcmp(s,"Maximum N for disk mascons:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &results.options.nmax);
    }
    //Now that the quantitative options are listed, here
    //come the path and file names:
    else if(!strcmp(s,"Output folder:\n")){
      f_return=fgets(s,max_length,in_fp);
      plot_options.outputfolder=s;
      //Remove the \n at the end of the line.
      if((pos=plot_options.outputfolder.find("\n"))!=(int)string::npos){
        plot_options.outputfolder.swap(plot_options.outputfolder.erase(pos,2));
      }
    }
    else if(!strcmp(s,"input folder:\n")){
      f_return=fgets(s,max_length,in_fp);
      input.options.basefolder=s;
      //Remove the \n at the end of the line.
      if((pos=input.options.basefolder.find("\n"))!=(int)string::npos){
        input.options.basefolder.swap(input.options.basefolder.erase(pos,2));
      }
    }
    else if(!strcmp(s,"input filename:\n")){
      f_return=fgets(s,max_length,in_fp);
      input.options.filename=s;
      //Remove the \n at the end of the line.
      if((pos=input.options.filename.find("\n"))!=(int)string::npos){
        input.options.filename.swap(input.options.filename.erase(pos,2));
      }
    }
    else if(!strcmp(s,"Tide gauge folder:\n")){
      f_return=fgets(s,max_length,in_fp);
      insitu.tg.basefolder=s;
      //Remove the \n at the end of the line.
      if((pos=insitu.tg.basefolder.find("\n"))!=(int)string::npos){
        insitu.tg.basefolder.swap(insitu.tg.basefolder.erase(pos,2));
      }
    }
    else if(!strcmp(s,"Topography type:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &ancillary.topo.type);
      grid.options.topo_type = ancillary.topo.type;
    }
    else if(!strcmp(s,"In situ cache folder:\n")){
      f_return=fgets(s,max_length,in_fp);
      insitu.cachefolder = gaiafolder();
      insitu.cachefolder.append(s);
      //Remove the \n at the end of the line.
      if((pos=insitu.cachefolder.find("\n"))!=(int)string::npos){
        insitu.cachefolder.swap(insitu.cachefolder.erase(pos,2));
      }
    }
    else if(!strcmp(s,"Grid cache folder:\n")){
      f_return=fgets(s,max_length,in_fp);
      grid.options.cachefolder = gaiafolder();
      grid.options.cachefolder.append(s);
      //Remove the \n at the end of the line.
      if((pos=grid.options.cachefolder.find("\n"))!=(int)string::npos){
        grid.options.cachefolder.swap(grid.options.cachefolder.erase(pos,2));
      }
    }
    else if(!strcmp(s,"Model cache folder:\n")){
      f_return=fgets(s,max_length,in_fp);
      model_options.cachefolder = gaiafolder();
      model_options.cachefolder.append(s);
      //Remove the \n at the end of the line.
      if((pos=model_options.cachefolder.find("\n"))!=(int)string::npos){
        model_options.cachefolder.swap(model_options.cachefolder.erase(pos,2));
      }
    }
    else if(!strcmp(s,"Region cache folder:\n")){
      f_return=fgets(s,max_length,in_fp);
      region_options.cachefolder = gaiafolder();
      region_options.cachefolder.append(s);
      //Remove the \n at the end of the line.
      if((pos=region_options.cachefolder.find("\n"))!=(int)string::npos){
        region_options.cachefolder.swap(region_options.cachefolder.erase(pos,2));
      }
    }
    else if(!strcmp(s,"Least squares cache folder:\n")){
      f_return=fgets(s,max_length,in_fp);
      results.options.least_squares_cachefolder = gaiafolder();
      results.options.least_squares_cachefolder.append(s);
      //Remove the \n at the end of the line.
      if((pos=results.options.least_squares_cachefolder.find("\n"))!=(int)string::npos){
        results.options.least_squares_cachefolder.swap(results.options.least_squares_cachefolder.erase(pos,2));
      }
    }
    else if(!strcmp(s,"input cache folder:\n")){
      f_return=fgets(s,max_length,in_fp);
      input.options.cachefolder=gaiafolder();
      input.options.cachefolder.append(s);
      //Remove the \n at the end of the line.
      if((pos=input.options.cachefolder.find("\n"))!=(int)string::npos){
        input.options.cachefolder.swap(input.options.cachefolder.erase(pos,2));
      }
    }
    else if(!strcmp(s,"arbitrary cache folder:\n")){
      f_return=fgets(s,max_length,in_fp);
      input.arbitrary.cachefolder=gaiafolder();
      input.arbitrary.cachefolder.append(s);
      //Remove the \n at the end of the line.
      if((pos=input.arbitrary.cachefolder.find("\n"))!=(int)string::npos){
        input.arbitrary.cachefolder.swap(input.arbitrary.cachefolder.erase(pos,2));
      }
    }
    else{
      cout<<"Did not recognize line #"<<i<<" in options file: "<<s<<endl;
      i--;//Didn't read data line this time.
    }
  }
  fclose(in_fp);

  //////////////////////////////////////////////////////////////////////////
  //Over-rides that affect different computers differently.
  //////////////////////////////////////////////////////////////////////////

  //If set to auto-select parallel or sequential region code, do so now.
  string gaiafoldername(gaiafolder());
  if(region_options.type != 0 and region_options.parallel == -1){
    //If "adata2" is present, this is probably Azonic so use parallel code.
    if(((int)gaiafoldername.find("adata2"))!=(int)string::npos){
      region_options.parallel = 1;
      if(verbose) cout<<"Auto-selected parallel code execution."<<endl;
    }
    //If "idata" is present, this is probably Iliff so use parallel code.
    else if(((int)gaiafoldername.find("idata"))!=(int)string::npos){
      region_options.parallel = 1;
      if(verbose) cout<<"Auto-selected parallel code execution."<<endl;
    }
    //If my first name is used, this is the laptop so use parallel code.
    else if(((int)gaiafoldername.find("bryan"))!=(int)string::npos){
      region_options.parallel = 1;
      if(verbose) cout<<"Auto-selected parallel code execution."<<endl;
    }
    else{
      region_options.parallel = 0;
      if(verbose) cout<<"Auto-select defaulted to sequential code execution."<<endl;
    }
  }

  //If set to auto-select number of parallel processes, do so now.
  if(results.options.numprocesses == -1  and region_options.parallel == 1){
    //If adata2 is used, this is probably Azonic so multiple processes
    if(((int)gaiafoldername.find("adata2"))!=(int)string::npos){
      results.options.numprocesses=3;
      if(verbose) cout<<"Auto-selected "<<results.options.numprocesses<<" processes."<<endl;
    }
    //If idata is used, this is probably Iliff so multiple processes
    else if(((int)gaiafoldername.find("idata"))!=(int)string::npos){
      results.options.numprocesses=3;
      if(verbose) cout<<"Auto-selected "<<results.options.numprocesses<<" processes."<<endl;
    }
    //If my first name is used, this is the laptop so use 2 processes.
    else if(((int)gaiafoldername.find("bryan"))!=(int)string::npos){
      results.options.numprocesses=2;
      if(verbose) cout<<"Auto-selected "<<results.options.numprocesses<<" processes."<<endl;
    }
    else{
      cout<<"!!WARNING! Number of parallel processes auto-select code failed!"<<endl;
    }
  }

  ////////////////////////////////////////////////////////////////////////
  //Run perform_analysis() but only to redefine params if necessary.
  //Type 0 is a special case where options need to be loaded from disk
  //and the grid itself can be changed.
  ////////////////////////////////////////////////////////////////////////
  results_s temp_results;
  ancillary_s temp_ancillary;
  grid_s temp_grid;
  grid_s temp_region;
  model_s temp_model;
  plot_options_s temp_plot_options;
  insitu_s temp_insitu;
  if(results.options.type == 0){
    //Be careful, if output/options.txt has type 0 then infinite loop!
    if(verbose) cout<<"Loading old grid and parameter options."<<endl;
    string input_file2(gaiafolder());//Filename of options file created in gaia

    //Load options from file in output/ which has grid options used in the
    //output files in same folder that will be loaded in perform_analysis()
    input_file2.append("output/options.txt");
    load_options(input_file2, input, temp_region.options, temp_ancillary, temp_grid, temp_model.options, temp_results, temp_plot_options, temp_insitu,1);

    //The only value in grid_options that should be retained is the
    //cachefolder, because the options.txt file in output/ may have been
    //produced on a different computer.
    temp_grid.options.cachefolder = grid.options.cachefolder;

    //Override grid.options.
    grid.options = temp_grid.options;

    //Override parameters.
    results.options.parameter      = temp_results.options.parameter;
    results.options.parameter_type = temp_results.options.parameter_type;
  }
  else{//In all other cases, run PART of perform_analysis to reset params.
    results.options.inside_load_options = 1;
    //Run part of perform_analysis() on the actual results struct so it can be changed.
    perform_analysis(input, temp_grid, temp_ancillary, temp_model, results, plot_options, temp_region, temp_insitu);
    results.options.inside_load_options = 0;
  }

  //If any arbitrary parameter types are requested, load the 
  //arbitrary time series so their titles and units are available.
  load_arbitrary_time_series(input, results);
  
  //Output vectors are set early so output_type.size() can guide calculations.
  //Creates output_type, output_tide, parameter_tide, titles, units, resizes
  //first index of results.outputs to same size as output_type vector.
  //Second indices are all set to grid.lat.size() in define_grid().
  create_output_vectors(results,input);
}

void load_ancillary(ancillary_s &ancillary, int verbose){
  /**********************************************************************
  Purpose: This function loads ancillary data from disk.  Ancillary data:

          Topography: Each point in topography has lat,lon coordinates and
          elevation (negative for water depth).  Also, structure holds
          "numpoints" to indicate number of points in these vectors.

          Temperature: FINISH!Each point in temp has lat,lon coordinates and
          temperature (in degrees C).  Also, structure holds "numpoints"
          to indicate number of points in these vectors.

          Also, options data for loading of all these ancillary data
          types is included inside each of their structures.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          int verbose - 1/0 turns on/off messages.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  double onelat,onelon,oneelev;
  char s[max_length];
  string basefolder(gaiafolder());
  string file;
  FILE *in_fp;

  ////////////////////////////////////////////////////////////////////////
  //Load topography
  ////////////////////////////////////////////////////////////////////////
  switch(ancillary.topo.type){
    default: cout<<"!!!!WARNING!!!!!! Topography type "<<ancillary.topo.type<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{//Sean Swenson's topography file.

      ancillary.topo.format = 0;//List of lats,lons,elevs.
      ancillary.topo.basefolder = "data/ancillary/";
      ancillary.topo.filename = "global_dem.txt";
      basefolder.append(ancillary.topo.basefolder);
      file = basefolder;
      file.append(ancillary.topo.filename);

      //Open input file for reading.
      in_fp = fopen(file.c_str(),"r");

      //Warn if file doesn't open correctly.
      if(in_fp == NULL) cout<<"The topography file, "<<ancillary.topo.filename<<", failed to open."<<endl;
      else if(verbose) cout<<"Loading topography data from file "<< ancillary.topo.filename<<"..."<<endl;

      //Read each line of data as a string, parse into numeric values,
      //then add each number to its respective vector.
      while(fgets(s,max_length,in_fp)!=NULL){
        sscanf(s,"%lf %lf %lf", &onelon, &onelat, &oneelev);
        ancillary.topo.lon.push_back(360.0 - onelon);
        ancillary.topo.lat.push_back(onelat);
        ancillary.topo.elev.push_back(oneelev);
      }

      fclose(in_fp);
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10:{//SRTM30+, global topo/bathy all in one big-endian binary file.

      //Obtained from ftp://topex.ucsd.edu/pub/srtm30_plus/
      //The  subdirectory called topo30 has the data
      //stored in a single large file of 2-byte integers
      //in MSB format (i.e. big-endian).  The grid spans
      //0 to 360 in longitude and -90 to 90 in latitude.
      //The upper left corner of the upper left grid cell
      //has latitude 90 and longitude 0.  There are
      //43200 columns and 21600 rows.

      long long i,j;

      ancillary.topo.format = 0;//List of lats,lons,elevs.
      ancillary.topo.format = 1;//Elevs in 2D equal-grid grid.
      //Save basefolder in model.options for future reference.
      ancillary.topo.basefolder = "data/ancillary/SRTM30Plus/";
      //"basefolder" contains the entire path.
      basefolder.append(ancillary.topo.basefolder);
      //Open input file for loading.
      file = basefolder;
      file.append("topo30.ascii");

      ancillary.topo.hr_lats.resize(21600);
      ancillary.topo.hr_lons.resize(43200);

      double minlat=-90, maxlat=90, minlon = 0, maxlon = 360;

      double lat_step = (maxlat-minlat)/(double)ancillary.topo.hr_lats.size();
      double lon_step = (maxlon-minlon)/(double)ancillary.topo.hr_lons.size();

      //Define latitudes, 0.5 means the north EDGE is at maxlat..
      for(j=0;j<(long long)ancillary.topo.hr_lats.size();j++){
        ancillary.topo.hr_lats.at(j) = maxlat - lat_step*((double)j+0.5);
      }

      //Define longitudes, 0.5 means the west EDGE is at minlon
      for(i=0;i<(long long)ancillary.topo.hr_lons.size();i++){
        ancillary.topo.hr_lons.at(i) = minlon + lon_step*((double)i+0.5);
      }

      //Temporary, to avoid using too much memory.
      ancillary.topo.hr_lats.resize(1);
      //short temp;

      //Allocate elev multivector.
      ancillary.topo.hr_elev.resize(ancillary.topo.hr_lats.size());
      for(j=0;j<(long long)ancillary.topo.hr_elev.size();j++) ancillary.topo.hr_elev.at(j).resize(ancillary.topo.hr_lons.size());

      //Set up binary reading routines.
      ifstream in(file.c_str(),ios_base::in | ios_base::binary);

      //Loop through latitudes.
      for(j=0;j<(long long)ancillary.topo.hr_lats.size();j++){
        //Read this row of points into hr_elev multivector.
        in.read((char*)&ancillary.topo.hr_elev[j][0], sizeof(short)*ancillary.topo.hr_elev.at(j).size());

        //Loop through longitudes.
        for(i=0;i<(long long)ancillary.topo.hr_lons.size();i++){
          //in.read((char*)&temp, sizeof(short));
          //if(i<20) cout<<"hr_elev["<<setw(5)<<j<<"]["<<setw(5)<<i<<"] = "<<temp<<" at lat "<<ancillary.topo.hr_lats.at(j)<<" and lon "<<ancillary.topo.hr_lons.at(i)<<endl;
          if(i<2410) cout<<"hr_elev["<<setw(5)<<j<<"]["<<setw(5)<<i<<"] = "<<ancillary.topo.hr_elev.at(j).at(i)<<" at lat "<<ancillary.topo.hr_lats.at(j)<<" and lon "<<ancillary.topo.hr_lons.at(i)<<endl;
          //cout<<"hr_elev["<<setw(5)<<j<<"]["<<setw(5)<<i<<"] = "<<ancillary.topo.hr_elev.at(j).at(i)<<endl;
        }
      }//End of loop through lats.*/

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 11:{//SRTM30+, topo/bathy to 45N all in one ascii file from Lin.

      //Obtained from ftp://topex.ucsd.edu/pub/srtm30_plus/
      //The  subdirectory called topo30 has the data
      //stored in a single large file of 2-byte integers
      //in MSB format (i.e. big-endian).  The grid spans
      //0 to 360 in longitude and -90 to 90 in latitude.
      //The upper left corner of the upper left grid cell
      //has latitude 90 and longitude 0.  There are
      //43200 columns and 21600 rows.

      //Lin wrote readtopo30 which converted it to ASCII.

      long long i,j;

      ancillary.topo.format = 1;//Elevs in 2D equal-grid grid.
      //Save basefolder in model.options for future reference.
      ancillary.topo.basefolder = "data/ancillary/SRTM30Plus/";
      //"basefolder" contains the entire path.
      basefolder.append(ancillary.topo.basefolder);
      //Open input file for loading.
      file = basefolder;
      file.append("topo30.ascii");

      ancillary.topo.hr_lats.resize(21600);
      ancillary.topo.hr_lons.resize(43200);

      //Need to have correct boundaries here.
      double minlat=-90, maxlat=90, minlon = 0, maxlon = 360;

      double lat_step = (maxlat-minlat)/(double)ancillary.topo.hr_lats.size();
      double lon_step = (maxlon-minlon)/(double)ancillary.topo.hr_lons.size();

      //Define latitudes, 0.5 means the north EDGE is at maxlat..
      for(j=0;j<(long long)ancillary.topo.hr_lats.size();j++){
        ancillary.topo.hr_lats.at(j) = maxlat - lat_step*((double)j+0.5);
      }

      //Define longitudes, 0.5 means the west EDGE is at minlon
      for(i=0;i<(long long)ancillary.topo.hr_lons.size();i++){
        ancillary.topo.hr_lons.at(i) = minlon + lon_step*((double)i+0.5);
      }

      //Trim lat vector so only loading from 90N to 45N.
      ancillary.topo.hr_lats.resize(21600/4);//Only down to 45N!

      //Allocate elev multivector.
      ancillary.topo.hr_elev.resize(ancillary.topo.hr_lats.size());
      for(j=0;j<(long long)ancillary.topo.hr_elev.size();j++) ancillary.topo.hr_elev.at(j).resize(ancillary.topo.hr_lons.size());

      //Open topo30.ascii file for reading.
      in_fp = fopen(file.c_str(),"r");

      //Warn if file doesn't open correctly.
      if(in_fp == NULL) cout << "The topo file, " << file
                       << ", failed to open."<<endl;
      else if(verbose) cout << "Loading topo data from file " << file <<"..."<<endl;

      char* f_return;//Used to stop fgets return value warning.
      //Loop through latitudes.
      for(j=0;j<(long long)ancillary.topo.hr_lats.size();j++){
        //Loop through latitudes.
        for(i=0;i<(long long)ancillary.topo.hr_lons.size();i++){
          f_return = fgets(s,max_length,in_fp);
          sscanf(s,"%hd", &ancillary.topo.hr_elev.at(j).at(i));
        }//End of loop through lons.
      }//End of loop through lats.
      fclose(in_fp);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 20:{//FES ascii data file, zeroing NP anomaly but no elev/depths.

      latlon_s rawfes;
      model_s model;
      long long i,j;

      //Save basefolder in model.options for future reference.
      ancillary.topo.basefolder = "data/FES2004/data/";
      //"basefolder" contains the entire path.
      basefolder.append(ancillary.topo.basefolder);
      model.options.type=1;//Pretend to be FES.
      define_model_titles_and_periods(model);

      ancillary.topo.filename = model.options.filenames.front();
      //Open input file for loading.
      file = basefolder;
      file.append(model.options.filenames.front());
      rawfes.filename = file;
      rawfes.discard_redundant_values = 1;
      read_rawfes(rawfes);

      //Loop through FES latitudes.
      for(j=0;j<(long long)rawfes.lat.size();j++){
        //Loop through FES longitudes.
        for(i=0;i<(long long)rawfes.lon.size();i++){
          ancillary.topo.lon.push_back(rawfes.lon.at(i));
          ancillary.topo.lat.push_back(rawfes.lat.at(j));
          //Add the point but zero any mask values, assume all N of 88N are ocean.
          if(rawfes.amplitudes.at(j).at(i) != rawfes.mask or rawfes.lat.at(j) > 88){
            ancillary.topo.elev.push_back(-666);
          }
          else{
            ancillary.topo.elev.push_back(666);
          }
        }//End of loop through FES lons.
      }//End of loop through FES lats.

      break;
    }
  }//End of switch-case.
  if(verbose) cout << "Finished loading topography data."<<endl;
  ////////////////////////////////////////////////////////////////////////
  //Load temperatures (FINISH!)
  ////////////////////////////////////////////////////////////////////////
  if(1==2){
  basefolder = gaiafolder();

  basefolder.append(ancillary.temp.basefolder);
  file = basefolder;
  file.append(ancillary.temp.filename);

  //Open input file for reading.
  in_fp = fopen(file.c_str(),"r");

  //Warn if file doesn't open correctly.
  if(in_fp == NULL) cout << "The temperatures file, " << ancillary.temp.filename
                   << ", failed to open."<<endl;
  else if(verbose) cout << "Loading temperature data from file " << ancillary.temp.filename <<"..."<<endl;

  //Read each line of data as a string, parse into numeric values,
  //then add each number to its respective vector.
  while(fgets(s,max_length,in_fp)!=NULL){
    sscanf(s,"%lf %lf %lf", &onelon, &onelat, &oneelev);
    ancillary.temp.lon.push_back(360.0 - onelon);
    ancillary.temp.lat.push_back(onelat);
    ancillary.temp.temp.push_back(oneelev);
  }

  fclose(in_fp);
  if(verbose) cout << "Finished loading temperatures."<<endl;
  }
}

void define_grid(grid_s &grid,
                 ancillary_s &ancillary,
                 results_s &results){
  /**********************************************************************
  Purpose: This function makes a grid based on input parameters passed
          to the function.  It's designed to be flexible, creating grids
          arranged anywhere on the globe in an equal-area, "equal-grid",
          or some other non-uniform fashion.  It returns a structure
          which holds all relevent details of the grid.

          The grid structure contains a simple 1d vector of grid points.
          This way the main program can simply iterate through the
          grid points without referring to the geometry of the grid.

          In order to speed up repeated runs with the same parameters,
          I've built in a cache that contains the results of previous
          grids.  The filenames of these cache files are encoded with
          the grid_options used to create them, so searching for a
          file that has matching options is straightforward.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  //These variables are used to search the cache folder for matching grids.
  DIR *cacheDIR;
  struct dirent *cacheDirEnt;
  //These variables are used to store a new cache file, or load an old one.
  string filename;//stores JUST the filename of cache file (not the path).
  //These variables were added in the process of xyz coord conversion.
  string temp_string;
  double topo_res;
  long long worst_case;

  //Automatic cap sizing can alter these variables.  Back them up!
  grid.options.original_cap_size = grid.options.cap_size;
  grid.options.original_lat_spacing = grid.options.lat_spacing;
  grid.options.original_lon_spacing = grid.options.lon_spacing;

  //If NOT using equal-grid grids, can't ask for latlon != 0.
  if(grid.options.type !=1 and grid.options.current_latlon!=0){
    grid.options.current_latlon = 0;
    cout<<"WARNING!!! Only equal-grid grids can use grid.options.current_latlon != 0, so latlon has been forced to 0."<<endl;
  }

  //If there's no cap, set its size to -1 to avoid cache ambiguity.
  if(grid.options.boundaries[1] != 90.0 and grid.options.boundaries[0] != -90.0) grid.options.cap_size = -1;

  //Search for previously computed grids in the cache, but only if
  //noload == 0 and this isn't a custom grid type.
  if(grid.options.noload == 0 and grid.options.type !=0){

    //Check to see if the cache contains a file with the same options
    //as the ones loaded in grid.options.  If so, set i=1.
    i=0;//Init as 0 because it hasn't found matching grid in cache yet.

    //First, figure out exactly what filename we're looking for by
    //creating a filename that corresponds to currently selected options.
    filename = create_grid_cache_filename(grid.options);
    //cout<<"Desired grid cache filename: "<<filename<<endl;

    //Open the cache folder.
    cacheDIR = opendir(grid.options.cachefolder.c_str());

    //Examine each file, see if it contains the desired grid.
    cacheDirEnt = readdir(cacheDIR);
    while(cacheDirEnt != NULL && i == 0){
      //If this filename matches desired grid options, set i=1 to exit loop.
      if(strcmp(filename.c_str(),cacheDirEnt->d_name)==0) i = 1;
      //cout<<"Potential grid cache filename: "<<cacheDirEnt->d_name<<endl;

      //printf("%s\n",cacheDirEnt->d_name);
      cacheDirEnt = readdir(cacheDIR);
    }

    // Release the open folder.
    closedir(cacheDIR);

    //If matching file was found, load it and skip rest of function.
    if(i == 1){
      load_grid_from_cache(grid);
      //Skip the loading code using a (gasp!) goto function.
      goto end_of_grid_loading;
    }
  }

  //If grid needs to be computed, layout points in manner described
  //by "type".
  cout<<"Defining grid..."<<endl;

  //If the grid cap_size is set to auto area selection (-1.0), use code
  //copied from define_grid type 2 to set the cap_size so cap has the same
  //area as nearby points.  The lat and lon spacings are tweaked as well.
  //Custom grid doesn't have a cap, and type 11 autosizes on its own.
  //Also, boundary[1] needs to be 90, otherwise there's no grid cap at all.
  if(grid.options.boundaries[1]==90 and grid.options.cap_size == -1.0 and grid.options.type != 0 and grid.options.type != 11){
    cout<<"Automating choice of grid cap_size to make polar caps have the same area as nearby grid points."<<endl;
    autosize_grid_cap(grid);
  }//End of grid cap size auto-set.

  switch(grid.options.type){
    default: cout<<"!!!!WARNING!!!!!! Grid type "<<grid.options.type<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 0:{//Custom grid.

      for(i=0;i<(long long)grid.lat.size();i++){
        //If the current lat value is "new" (not in different_lats)
        //then push_back the current lat.
        if(count(grid.different_lats.begin(),grid.different_lats.end(),grid.lat.at(i)) == 0){
          grid.different_lats.push_back(grid.lat.at(i));
        }

        grid.lat_halfwidth.push_back(grid.options.lat_spacing*km2deg/2.0);
        if(grid.lat.at(i) < 90.0 and grid.lat.at(i) > -90.0)
          grid.lon_halfwidth.push_back(grid.options.lon_spacing*km2deg/2.0/cos(deg2rad*grid.lat.at(i)));
        else
          grid.lon_halfwidth.push_back(180.0);

      }
      //Calculate area, numerical factor between cm thickness and mass.
      grid_cm2kg(grid);

      calculate_grid_topography2(grid,ancillary);

      //Calculate xyz coords of each grid point.
      calculate_grid_xyz(grid);
      printf("\nFinished.  %lld custom grid points defined.\n",(long long)grid.lat.size());

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{//"Equal grid" grid with cap over north pole.

      //Distribute points over the northern hemisphere, along with a cap
      //on the north pole. Defines lat,lon,diff_lat,halfwidth vectors.
      northern_latlons(grid);

      //If latlon != 0, skip all the rest.
      if(grid.options.current_latlon != 0) goto end_of_latlon_building;

      //Calculate area, numerical factor between cm thickness and mass.
      grid_cm2kg(grid);

      calculate_grid_topography2(grid,ancillary);

      //Calculate xyz coords of each grid point.
      calculate_grid_xyz(grid);

      cout<<"Finished grid. "<<grid.lat.size()<<" grid points defined."<<endl;
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 2:{ //"Pseudo-equal area" grid with cap over north pole.
             //This is a modification of equal-area to remove the
             //gaps around 0E- every latitude line has a number of
             //grid points that is as close as possible to being
             //"equal area" while still filling up the available
             //space with equal-sized bins.

      //Distribute points over the northern hemisphere, along with a cap
      //on the north pole. Defines lat,lon,diff_lat,halfwidth vectors.
      northern_latlons(grid);

      //Calculate area, numerical factor between cm thickness and mass.
      grid_cm2kg(grid);

      cout<<"WARNING! wcover HASN'T YET BEEN FILLED IN!"<<endl;
      calculate_grid_topography(grid,ancillary);

      //Calculate xyz coords of each grid point.
      calculate_grid_xyz(grid);

      cout<<"Finished grid. "<<grid.lat.size()<<" grid points defined."<<endl;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 3:{//Based on 2, but gr_op.specified_lat is used to rotate around
            //x axis. spec_lon is set to 0, most other types set spec_lat=0
            //THIS TYPE DELETES ALL GR PTS THAT ROTATE SOUTH OF BOUNDARY[0]!

      //Because the grid will be rotated, need to define pts further
      //south than min_lat alone. Be careful about negative angles!
      if(grid.options.specified_lat > 0)
        grid.options.boundaries[0] -= grid.options.specified_lat;
      else
        grid.options.boundaries[0] += grid.options.specified_lat;

      if(grid.options.boundaries[0] < 0){
        cout<<"!!!WARNING!!! Rotation will bring grid points from latitude "<<grid.options.boundaries[0]<<" into the boundaries, which requires the southern grid code!"<<endl;
      }

      //Distribute points over the northern hemisphere, along with a cap
      //on the north pole. Defines lat,lon,diff_lat,halfwidth vectors.
      northern_latlons(grid);

      //After distribution of pts, return min_lat to its original value.
      //Be careful about neg angles!
      if(grid.options.specified_lat > 0)
        grid.options.boundaries[0] += grid.options.specified_lat;
      else
        grid.options.boundaries[0] -= grid.options.specified_lat;

      //Needs to be done before rotation!
      grid_cm2kg(grid);

      grid.rotate = grid.options.specified_lat;
      cout<<"Grid is being rotated by "<<grid.rotate<<" degrees about the x axis."<<endl;
      rotate_grid_about_x(grid);
      delete_grid_points_outside_boundaries(grid);

      calculate_grid_topography(grid,ancillary);

      //This hasn't been written yet, but this is where a mask like the
      //ocean mask should be applied. It requires that grid.elev be
      //filled in, and this should be done before xyz because xyz is slow.
      //Might be pretty easy to write- I can assume a certain set of
      //vectors are filled in, and maybe write a quick test to make sure
      //the relevant vectors are the same length?

      //apply_ocean_mask(grid);

      calculate_grid_xyz(grid);

      cout<<"Finished grid. "<<grid.lat.size()<<" grid points defined."<<endl;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10:{//Based on type 2, but uses new topo routine to calc
             //wcover/mean_depth and caps distances at lat_spacing.

      //Distribute points over the northern hemisphere, along with a cap
      //on the north pole. Defines lat,lon,diff_lat,halfwidth vectors.
      northern_latlons(grid);

      //Calculate area, numerical factor between cm thickness and mass.
      grid_cm2kg(grid);

      calculate_grid_topography2(grid,ancillary);

      //Calculate xyz coords of each grid point.
      calculate_grid_xyz(grid);

      cout<<"Finished grid. "<<grid.lat.size()<<" grid points defined."<<endl;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 11:{//Based on 10, shifted so specified_lat/lon has pt on it.

      //Distribute northern latlons so specified lat/lon is on grid pt.
      //In kilometers. Grids can use small tolerance.
      grid.options.specified_tolerance = 0.1;
      grid.options.base = "grid";
      snap_grid_to_point(grid);

      //Calculate area, numerical factor between cm thickness and mass.
      grid_cm2kg(grid);

      calculate_grid_topography2(grid,ancillary);

      //Calculate xyz coords of each grid point.
      calculate_grid_xyz(grid);

      cout<<"Finished grid. "<<grid.lat.size()<<" grid points defined."<<endl;

      break;
    }
  }//End of switch-case.

  //After creating grid, save it in the cache so it can be
  //retrieved more quickly the next time this same grid is requested.
  //But only if this isn't a custom grid type.
  if(grid.options.type !=0){
    write_grid_to_cache(grid);
    cout<<"Reloading grid to force grids calculated from scratch to be identical to grids loaded from cache."<<endl;
    grid_s empty_grid;
    empty_grid.options = grid.options;
    grid = empty_grid;
    load_grid_from_cache(grid);
  }

  end_of_grid_loading://Successful loading code jumps to this point.

  //Grids with spacings less than 50km may have problems with ocean points'
  //distance_to_ocean being large compared to spacing. They'll be set to
  //zero here for either land/ocean pts so these comparisons don't keep
  //screwing up, but the info is always available in the grid up to this
  //point.
  //Actually, the grid will be searched for the largest topo distance
  //(distocean for elev<0, distland for elev>0) then ANY distances smaller
  //than this will be set to zero. This not only removes burden of
  //remembering to update topo resolution but also prevents having to
  //insert lots of if statements checking for distances compared to
  //the current topo res.
  topo_res=0.0;
  worst_case=-1;
  for(i=0;i<(long long)grid.lat.size();i++){
    if(grid.elev.at(i) < 0){
      //Custom grids need >= because some custom grids are exactly on FES topo pts.
      if(grid.distance_to_ocean.at(i) >= topo_res){
        topo_res = grid.distance_to_ocean.at(i);
        worst_case = i;
        //cout<<"Current resolution of "<<topo_res<<" km"<<" at grid lat "<<grid.lat.at(worst_case)<<" and lon "<<grid.lon.at(worst_case)<<endl;
      }
    }
    else{
      if(grid.distance_to_land.at(i) >= topo_res){
        topo_res = grid.distance_to_land.at(i);
        worst_case = i;
        //cout<<"Current resolution of "<<topo_res<<" km"<<" at grid lat "<<grid.lat.at(worst_case)<<" and lon "<<grid.lon.at(worst_case)<<endl;
      }
    }
  }

  for(i=0;i<(long long)grid.lat.size();i++){
    if(grid.distance_to_ocean.at(i) < topo_res)
      grid.distance_to_ocean.at(i) = 0.0;
    if(grid.distance_to_land.at(i) < topo_res)
      grid.distance_to_land.at(i) = 0.0;
  }
  cout<<"Topography data has a worst-case resolution of "<<topo_res<<" km"<<" at grid lat "<<grid.lat.at(worst_case)<<" and lon "<<grid.lon.at(worst_case)<<endl;

  //Briefly describe autosizing, or lack thereof.
  if(grid.options.original_cap_size == -1.0){
    cout<<"Original grid called for lat_spacing "<<fixed<<setprecision(2)<<setw(8)<<grid.options.original_lat_spacing<<" km, lon_spacing "<<grid.options.original_lon_spacing<<" km, cap_size "<<grid.options.original_cap_size<<" km, while autosizing gave the final grid lat_spacing "<<grid.options.lat_spacing<<" km, lon_spacing "<<grid.options.lon_spacing<<" km, cap_size "<<grid.options.cap_size<<" km."<<endl;
    cout<<"lat_spacing change: "<<fixed<<setprecision(2)<<100*(grid.options.lat_spacing/grid.options.original_lat_spacing-1)<<"%, lon_spacing change: "<<100*(grid.options.lon_spacing/grid.options.original_lon_spacing-1)<<"%."<<endl;
  }
  else{
    cout<<"Grid called for (and got- no autosizing) lat_spacing "<<fixed<<setprecision(1)<<setw(7)<<grid.options.original_lat_spacing<<" km, lon_spacing "<<grid.options.original_lon_spacing<<" km, cap_size "<<fixed<<setprecision(1)<<setw(7)<<grid.options.original_cap_size<<" km."<<endl;
    if(grid.options.original_cap_size != grid.options.cap_size or grid.options.original_lat_spacing != grid.options.lat_spacing or grid.options.original_lon_spacing != grid.options.lon_spacing)
      cout<<"!!!!WARNING!!! SHOULD NEVER SEE THIS KDS872NFY273HFJD8"<<endl;
  }

  //Now resize the second dimension (have to do this multiple times)
  //so that the second dimension holds all grid points.
  //Note that the first dimension was set in create_output_vectors().
  for(i=0;i<(long long)results.options.output_type.size();i++){
    results.outputs.at(i).resize(grid.lat.size(),0.0);
  }

  end_of_latlon_building://Latlon code jumps to this point to avoid unstructured grid code.

  cout<<"Grid is now finished."<<endl;

  //for(i=0;i<(long long)grid.lat.size();i++) printf("Pt (%4.1f,%5.1f) has %.2f%% wc, %.0fm depth.\n", grid.lat.at(i), grid.lon.at(i), grid.wcover.at(i)*100.0, grid.wdepth.at(i));
}

void snap_grid_to_point(grid_s &grid){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i, index;
  int verbose = 1;

  //Distribute points over the northern hemisphere, along with a cap
  //on the north pole. Defines lat,lon,diff_lat,halfwidth vectors.
  northern_latlons(grid);

  //Check to see how far away the nearest grid point is before snapping.
  if(verbose) cout<<"Before "<<grid.options.base<<" snap. Specified pt at "<<grid.options.specified_lat<<", "<<grid.options.specified_lon<<". ";
  index = grid_index_via_distance(grid, grid.options.specified_lat, grid.options.specified_lon, verbose);

  //Make sure specified_lat is at least lat_spacing away from north pole.
  if((90.0-grid.options.specified_lat)*deg2km<grid.options.lat_spacing){
    cout<<"!!!WARNING!!! Specified lat is only "<<(90.0-grid.options.specified_lat)*deg2km<<" km away from NP, while lat_spacing is "<<grid.options.lat_spacing<<" km. Reduce the lat_spacing in order to specify this point."<<endl;
  }

  //Iterates through cap_size and lat_spacing to get a line of grid points exactly
  //on specified_lat.
  cout<<"Automating choice of "<<grid.options.base<<" cap_size, lat_spacing to put "<<grid.options.base<<" points directly on specified_lat "<<setprecision(5)<<grid.options.specified_lat<<"."<<endl;
  autosnap_grid_to_lat(grid);

  //Distribute points over the northern hemisphere, along with a cap
  //on the north pole. Defines lat,lon,diff_lat,halfwidth vectors.
  northern_latlons(grid);

  //Now that latitude has been altered, longitudes need to be offset.
  //First, find closest latitude (ought to be the same as specified_lat.)
  index = index_of_closest_match(grid.different_lats,grid.options.specified_lat);
  //Build vector of lons at this lat.
  vector<double> lons;
  for(i=0;i<(long long)grid.lat.size();i++){
    if(grid.lat.at(i)==grid.different_lats.at(index)) lons.push_back(grid.lon.at(i));
  }

  //Now find closest longitude at this lat.
  index = index_of_closest_match(lons,grid.options.specified_lon);
  grid.rotate = grid.options.specified_lon - lons.at(index);

  if(verbose) cout<<"The "<<grid.options.base<<" is being rotated by "<<grid.rotate<<" degrees about the z axis."<<endl;
  rotate_grid_about_z(grid);

  //Check to see how far away the nearest grid point is after snapping.
  if(verbose) cout<<"After "<<grid.options.base<<" snap. Specified pt at "<<grid.options.specified_lat<<", "<<grid.options.specified_lon<<". ";
  index = grid_index_via_distance(grid, grid.options.specified_lat, grid.options.specified_lon, verbose);

}

void snap_region_to_point(grid_s &region){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  grid_s grid; //Temporary grid.

  grid.options  = region.options;

  //Now use the regionified temp grid to lay out the points for the regions.
  //In kilometers. Regions NEED large tolerance.
  grid.options.specified_tolerance = 10;
  grid.options.base = "region";
  snap_grid_to_point(grid);

  //After grid lays out points, copy all relevant information to region.
  region.options.cap_size    = grid.options.cap_size;
  region.options.lat_spacing = grid.options.lat_spacing;
  region.options.lon_spacing = grid.options.lon_spacing;
  region.lat = grid.lat;
  region.lon = grid.lon;
  region.lat_halfwidth = grid.lat_halfwidth;
  region.lon_halfwidth = grid.lon_halfwidth;
  region.different_lats = grid.different_lats;
}

void autosnap_grid_to_lat(grid_s &grid){
  /***********************************************************************
  Purpose: This function is called by snap_grid_to_point() and iterates
           through grid cap sizes and lat spacings to put grid points
           exactly on specified_lat.
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  grid_s autogrid;
  autogrid.options=grid.options;

  int converged=0;//Used to control outer iteration loop.
  long long l=0;//Tracks how many times outer while loop has run.
  double spacing_jitter=1E-2;//Resolution of spacings in cache filename.
  //Iterate the lat/lon_spacings around the specified value until
  //the cap_size iteration converges.
  while(converged == 0){
    l++;

    //Need to reset these on each outer loop.
    autogrid.options.lat_spacing = autogrid.options.original_lat_spacing;
    autogrid.options.lon_spacing = autogrid.options.original_lon_spacing;

    //Now jitter the lat_spacing (and lon_spacing if they're identical)
    if(l > 1){//Only jitter if this isn't the first loop.
      if(grid.options.original_lat_spacing == grid.options.original_lon_spacing){
        //If l is even, divide l by 2 and multiply by spacing_jitter,
        //then subtract from orig_lat/lon_spacing to get lat/lon_spacing.
        if(l % 2 == 0){
          autogrid.options.lat_spacing -= (double)(l/2)*spacing_jitter;
          autogrid.options.lon_spacing -= (double)(l/2)*spacing_jitter;
        }
        //Otherwise, odd l values are incremented, divided by 2, then ADDED.
        else{
          autogrid.options.lat_spacing += (double)((l+1)/2)*spacing_jitter;
          autogrid.options.lon_spacing += (double)((l+1)/2)*spacing_jitter;
        }
      }//End of "if orig lat/lon spacings are identical".
      else{
        //If l is even, divide l by 2 and multiply by spacing_jitter,
        //then subtract from orig_lat/lon_spacing to get lat/lon_spacing.
        if(l % 2 == 0)
          autogrid.options.lat_spacing -= (double)(l/2)*spacing_jitter;
        //Otherwise, odd l values are incremented, divided by 2, then ADDED.
        else
          autogrid.options.lat_spacing += (double)((l+1)/2)*spacing_jitter;
      }//End of "if orig lat/lon spacings are NOT identical."
    }

    //cout<<endl<<"Outer iteration loop #"<<l<<" has changed lat_spacing from "<<fixed<<setprecision(2)<<setw(7)<<autogrid.options.original_lat_spacing<<" to "<<autogrid.options.lat_spacing<<endl<<endl;

    //This is 1st guess based on lat_spacing.
    //0.5 factor makes the area sorta close...
    autogrid.options.cap_size = 0.5*autogrid.options.lat_spacing;

    //Distribute points over the northern hemisphere, along with a cap
    //on the north pole. Defines lat,lon,diff_lat,halfwidth vectors.
    northern_latlons(autogrid);

    //Determine which difflat is just north of speclat, expand cap_size
    //to make them equal.
    long long index = index_of_closest_match(autogrid.different_lats,autogrid.options.specified_lat);
    //cout<<"speclat: "<<autogrid.options.specified_lat<<", matching_lat: "<<autogrid.different_lats.at(index)<<endl;
    //Need to initialize the offset so the while loop starts.
    double offset = (autogrid.different_lats.at(index) - autogrid.options.specified_lat)*deg2km;
    long long areacounter=0, max_iterations=20;
    while(fabs(offset)>grid.options.specified_tolerance and areacounter<max_iterations){

      //Only modify cap_size if at least one loop has been made.
      if(areacounter>0){
        autogrid.options.cap_size += offset;
        //cout<<"Grid cap_size is being expanded by "<<offset<<" km to "<<autogrid.options.cap_size<<endl;
      }

      areacounter++;
      //cout<<"For inner iteration "<<areacounter<<", autogrid.options.cap_size = "<<autogrid.options.cap_size<<" km."<<endl;

      //Distribute points over the northern hemisphere, along with a cap
      //on the north pole. Defines lat,lon,diff_lat,halfwidth vectors.
      northern_latlons(autogrid);

      //Determine which difflat is just north of speclat, expand cap_size
      //to make them equal.
      index = index_of_closest_match(autogrid.different_lats,autogrid.options.specified_lat);
      //cout<<"speclat: "<<autogrid.options.specified_lat<<", matching_lat: "<<autogrid.different_lats.at(index)<<endl;

      //Need to initialize the offset so the while loop starts.
      offset = (autogrid.different_lats.at(index) - autogrid.options.specified_lat)*deg2km;

    }//End of "while offset > grid.options.specified_tolerance"

    if(fabs(offset)>grid.options.specified_tolerance){
      converged=0;
      //cout<<"!!WARNING!! "<<areacounter<<" iterations were performed but lats are STILL out of tolerance!"<<endl;
    }
    else converged=1;
  }//End of outer loop that jitters the lat/lon spacings.

  grid.options.cap_size = autogrid.options.cap_size;
  grid.options.lat_spacing = autogrid.options.lat_spacing;
  grid.options.lon_spacing = autogrid.options.lon_spacing;
  cout<<"Autosnap iterated "<<l<<" times."<<endl;
}

long long index_of_closest_match(vector<double> &dbls, double &temp){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i,index=0;
  double mismatch = fabs(dbls.front()-temp);//Init to one of possibly lowest vals.

  for(i=0;i<(long long)dbls.size();i++){
    //cout<<"dbls["<<i<<"]: "<<dbls.at(i)<<endl;
    if(fabs(dbls.at(i)-temp)<mismatch) index=i;
    mismatch = fabs(dbls.at(i)-temp);
  }

  return index;
}

void rotate_grid_about_x(grid_s &grid){
  /***********************************************************************
  Purpose: This function rotates the grid latlons about the x axis, which
           if used with a positive grid.rotate, sends the north pole into
           Hudson Bay. Requires converting to xyz coords, so it's slow.
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i;

  transform_s transform;
  transform.init=0;//Probably not necessary, but why not?
  transform.theta=grid.rotate;//Specified in degrees.

  //Initialize variables for notification function for time remaining.
  notification_s notification;
  //1st notification occurs after two loops, but it usually won't print
  //anything because not enough total_time will have passed (first_interval).
  //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
  notification.interval=2;
  //Desired min and max intervals in seconds b/w notifications.
  notification.first_interval=10;
  notification.min_interval=60, notification.max_interval=90;
  //notification.min_interval=30, notification.max_interval=90;
  notification.start_time=-1; notification.current_time=-1;
  notification.previous_time=-1;//Needs to be initialized
  notification.prefix = "Grid rotation";

  notification.starting_index = 0;
  notification.partial_saves_enabled = 0;

  //Used to size notification intervals.
  notification.previous_i=notification.starting_index;
  notification.num_loops = (long long)grid.lat.size()-notification.starting_index;
  //notification.interval will not be allowed to go above this number
  //so that at LEAST a certain number of messages are seen.
  notification.max_notif_interval=(long long)(notification.num_loops/4);

  //Annoyingly, these need to be defined! Argh!
  least_squares_s dummy_ls;
  input_s dummy_input;
  results_s dummy_ar;

  notification.start_time = time(NULL);

  for(i=0;i<(long long)grid.lat.size();i++){

    notify(notification,i,dummy_ls,dummy_input,dummy_ar);

    transform.lat = grid.lat.at(i);
    transform.lon = grid.lon.at(i);

    //Convert lat/lon to SPHERICAL xyz.
    spherical_latlon2xyz(transform);

    //Rotate this pt.
    rotate_xyz_about_x(transform);

    //Convert SPHERICAL xyz back to lat/lon.
    spherical_xyz2latlon(transform);

    grid.lat.at(i) = transform.lat;
    grid.lon.at(i) = transform.lon;

    //Good time to change these, which should make binning faster.
    grid.lon_halfwidth.at(i) = grid.options.lon_spacing*km2deg/2.0/cos(deg2rad*grid.lat.at(i));

  }

  notification.end_time = time(NULL);
  notification.time_total=notification.end_time-notification.start_time;
  cout<<notification.prefix<<" took "
    <<seconds_to_string(notification.time_total)<<endl;
}

void rotate_grid_about_z(grid_s &grid){
  /***********************************************************************
  Purpose: This function rotates the grid latlons about z axis, which is
           very simple because it just requires adding grid.rotate degs
           to each longitude except for the cap(s).
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i;
  //This rotation only touches lons, but checks lats to avoid rotating cap
  for(i=0;i<(long long)grid.lat.size();i++){
    //Don't rotate cap b/c is_pt_in_bin() needs caps' lons to be 180.0.
    if(fabs(grid.lat.at(i)) != 90.0){
      grid.lon.at(i) = restrict_degrees360(grid.lon.at(i)+grid.rotate);
    }
  }
}

void northern_latlons(grid_s &grid){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i,im,j,jm;
  double current_lat,current_lon;
  double current_lat_halfwidth,current_lon_halfwidth;
  double min_lat,max_lat,min_lon,max_lon;
  double np_offset;

  //Shouldn't have to worry about externally clearing these vectors
  //New grid variables should be added here.
  grid.lat.clear();
  grid.lon.clear();
  grid.different_lats.clear();
  grid.lat_halfwidth.clear();
  grid.lon_halfwidth.clear();
  grid.area.clear();
  grid.density.clear();
  grid.cm2kg.clear();
  grid.distance_to_ocean.clear();
  grid.distance_to_land.clear();
  grid.wcover.clear();
  grid.wdepth.clear();
  grid.x.clear();
  grid.y.clear();
  grid.z.clear();

  //This is NORTHERN latlons. No southern hemisphere points allowed!
  if(grid.options.boundaries[0] < 0 or grid.options.boundaries[1] < 0){
    cout<<"WARNING! northern_latlons can't use boundaries south of the equator!"<<endl;
    return;
  }

  //If latlon != 0, record as latlon and skip all the rest.
  if(grid.options.current_latlon != 0){
    double latw = grid.options.lat_spacing*km2deg;
    //Define the extent of the edges of each bin.
    min_lat = grid.options.boundaries[0];
    max_lat = grid.options.boundaries[1];
    //Different than even type 1 grids because these aren't for mascons,
    //they're for plotting so they should extend all the way to the boundaries.
    //That's why the +1 and -1 are there, to extend the grid to the boundaries.
    jm = (long long)((max_lat-min_lat)/latw)+1;
    //This next line adjusts the latitude width so that all space is filled- no gaps.
    if(jm>0) latw = (max_lat-min_lat)/(jm-1);
    else latw = 0.0;
    double lonw = grid.options.lon_spacing*km2deg;
    //Define the extent of the edges of each bin.
    min_lon = grid.options.boundaries[2];
    max_lon = grid.options.boundaries[3];
    im = (long long)((max_lon-min_lon)/lonw)+1;
    //This next line adjusts the longitude width so that all space is filled- no gaps.
    if(im>0) lonw = (max_lon-min_lon)/(im-1);
    else lonw = 0.0;
    grid.latlon.lat.clear();
    grid.latlon.lon.clear();
    //Fill lat,lon vectors before exiting, which is why "j" is used here AND in outer loop.
    //Notice that lat is "backwards" but it matches the layout of the unstructured grid
    //which starts at the NP because the original JPL data only covered lat>50N.
    for(j=0;j<jm;j++) grid.latlon.lat.push_back(max_lat-j*latw);
    for(i=0;i<im;i++) grid.latlon.lon.push_back(min_lon+i*lonw);
    //for(j=0;j<(long long)grid.latlon.lon.size();j++) cout<<"grid.latlon.lon["<<j<<"] = "<<grid.latlon.lon[j]<<endl;
    return;
  }

  //If northern boundary==90, define cap on north pole to be first grid point.
  if(grid.options.boundaries[1]==90.0){
    np_offset = grid.options.cap_size*km2deg;
    current_lat = 90.0;
    current_lon = 180.0;//In "middle" of region so it covers 0-360.
    grid.lat.push_back(current_lat);
    grid.different_lats.push_back(current_lat);
    grid.lon.push_back(current_lon);
    //These next values aren't technically needed or used, but they're
    //necessary as placeholders.
    grid.lat_halfwidth.push_back(grid.options.cap_size*km2deg);
    grid.lon_halfwidth.push_back(180.0);
  }
  else{//If northern boundary != 90.0
    np_offset = 0.0;
  }

  //Other points lie between the specified boundaries.
  //Calculate lat_halfwidth in degrees for these
  //purposes, based on lat_spacing given in km.
  //Note - min_lon and max_lon are different at each latitude,
  //therefore they must be calculated inside the latitude loop.
  current_lat_halfwidth = grid.options.lat_spacing*km2deg/2.0;
  //Define the extent of the edges of each bin.
  min_lat = grid.options.boundaries[0];
  max_lat = grid.options.boundaries[1] - np_offset;
  jm = (long long)((max_lat-min_lat)/(2*current_lat_halfwidth));
  //This next line rounds the halfwidth to the correct value
  //so that all space is filled- no gaps.
  if(jm>0) current_lat_halfwidth = (max_lat-min_lat)/jm/2;
  else current_lat_halfwidth = 0.0;
  //Now that the halfwidth has been defined, apply it to the
  //boundaries of the grid, in terms of the centers of the bins.
  min_lat = grid.options.boundaries[0] + current_lat_halfwidth;
  max_lat = grid.options.boundaries[1] - current_lat_halfwidth - np_offset;
      
  //Record different lats, starting from the cap south.
  for(j=0;j<jm;j++) grid.different_lats.push_back(max_lat-j*2*current_lat_halfwidth);

  int verbose = 0;
  //Loop through latitudes, starting next to the cap.
  if(verbose) cout<<"lat_halfwidth: "<<current_lat_halfwidth<<" jm: "<<jm<<endl;
  for(j=0;j<jm;j++){
    if(verbose) printf("Grid is %.0f%% complete.",100.0*j/jm);
    if(verbose) cout<<endl;
    current_lat = max_lat-j*2*current_lat_halfwidth;
    if(grid.options.type == 1){//Assume this is the only equal-grid type...
      current_lon_halfwidth = grid.options.lon_spacing*km2deg/2.0;
    }
    else{//... and that all other types are equal-area.
      //min_lon and max_lon need to be recalculated at each latitude
      //because lon_halfwidth is different at different latitudes.
      //Using cos(lat) b/c sin(lat) would only work for colatitude.
      current_lon_halfwidth = grid.options.lon_spacing*km2deg/2.0/cos(deg2rad*current_lat);
    }
    //Define the extent of the edges of each bin.
    min_lon = grid.options.boundaries[2];
    max_lon = grid.options.boundaries[3];
    im = (long long)((max_lon-min_lon)/(2*current_lon_halfwidth));
    //This next line rounds the halfwidth to the correct value
    //so that all space is filled- no gaps.
    current_lon_halfwidth = (max_lon-min_lon)/im/2;
    //Now that the halfwidth has been defined, apply it to the
    //boundaries of the grid, in terms of the centers of the bins.
    min_lon = grid.options.boundaries[2] + current_lon_halfwidth;
    max_lon = grid.options.boundaries[3] - current_lon_halfwidth;
    
    if(verbose) cout<<"lon_halfwidth: "<<current_lon_halfwidth<<" im: "<<im<<endl;
    //Loop through longitudes, starting at min_lon.
    for(i=0;i<im;i++){
      current_lon = min_lon+i*2*current_lon_halfwidth;
      if(verbose) cout<<"lat, lon: "<<current_lat<<", "<<current_lon<<endl;

      grid.lat.push_back(current_lat);
      grid.lon.push_back(current_lon);

      grid.lat_halfwidth.push_back(current_lat_halfwidth);
      grid.lon_halfwidth.push_back(current_lon_halfwidth);
    }//End of longitude loop.
  }//End of latitude loop.
}

void southern_latlons(grid_s &grid){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i;
  //This is SOUTHERN latlons. No northern hemisphere points allowed!
  if(grid.options.boundaries[0] > 0 or grid.options.boundaries[1] > 0){
    cout<<"WARNING! southern_latlons can't use boundaries north of the equator!"<<endl;
    return;
  }

  grid_s sgrid = grid;

  //Change southern latitudes to equivalent northern latitudes so
  //northern_latlons can be used, but do it in sgrid so this local change
  //isn't applied to the grid that other functions will see.
  sgrid.options.boundaries[0] *= -1.0;
  sgrid.options.boundaries[1] *= -1.0;
  
  northern_latlons(sgrid);
  
  //New grid variables should be added here.
  if(grid.options.current_latlon != 0){
    //Multiply lat vector by -1 to switch to southern hemisphere.
    grid.latlon.lat = sgrid.latlon.lat;
    for(i=0;i<(long long)grid.latlon.lat.size();i++) grid.latlon.lat.at(i) *= -1.0;
    //No need to change this vector.
    grid.latlon.lon = sgrid.latlon.lon;
  }
  else{
    //Multiply lat vectors by -1 to switch to southern hemisphere.
    grid.lat = sgrid.lat;
    for(i=0;i<(long long)grid.lat.size();i++) grid.lat.at(i) *= -1.0;
    grid.different_lats = sgrid.different_lats;
    for(i=0;i<(long long)grid.different_lats.size();i++) grid.different_lats.at(i) *= -1.0;
    //No need to change these vectors.
    grid.lon = sgrid.lon;
    grid.lat_halfwidth = sgrid.lat_halfwidth;
    grid.lon_halfwidth = sgrid.lon_halfwidth;
  }
}

void delete_grid_points_outside_boundaries(grid_s &grid){
  /***********************************************************************
  Purpose: This function is used after grid is rotated about x, so that
           points that used to be inside the specified grid boundaries
           are now outside of them.
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i;

  //Start at end to prevent deletions from shifting subsequent pts!
  for(i=(long long)grid.lat.size()-1;i>=0;i--){
    //Check for out-of-bounds points.
    //boundaries order: [min_lat, max_lat, min_lon, max_lon]
    //cout<<"min_lat: "<<grid.options.boundaries[0]<<" min_lat: "<<grid.options.boundaries[1]<<endl;
    //cout<<"min_lon: "<<grid.options.boundaries[2]<<" min_lon: "<<grid.options.boundaries[3]<<endl;
    if(grid.lat.at(i) < grid.options.boundaries[0] or grid.lat.at(i) > grid.options.boundaries[1] or grid.lon.at(i) < grid.options.boundaries[2] or grid.lon.at(i) > grid.options.boundaries[3]){
      //cout<<"deleted lat/lon: "<<grid.lat.at(i)<<" / "<<grid.lon.at(i)<<endl;
      //New grid variables should be added here.
      //Not including xyz because they haven't been calced yet.
      grid.lat.erase(grid.lat.begin()+i);
      grid.lon.erase(grid.lon.begin()+i);
      grid.lat_halfwidth.erase(grid.lat_halfwidth.begin()+i);
      grid.lon_halfwidth.erase(grid.lon_halfwidth.begin()+i);
      grid.area.erase(grid.area.begin()+i);
      grid.density.erase(grid.density.begin()+i);
      grid.cm2kg.erase(grid.cm2kg.begin()+i);
      if(grid.wcover.size() > 0) grid.wcover.erase(grid.wcover.begin()+i);
      if(grid.wdepth.size() > 0) grid.wdepth.erase(grid.wdepth.begin()+i);
    }
  }
}

void calculate_grid_xyz(grid_s &grid){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i;
  grid_s temp_grid;
  string temp_string;
  double x,y,z;

  //Need to be empty!
  grid.x.clear();
  grid.y.clear();
  grid.z.clear();

  //Faster than push_back()!
  grid.x.resize(grid.lat.size(),0);
  grid.y.resize(grid.lat.size(),0);
  grid.z.resize(grid.lat.size(),0);

  //Initialize variables for notification function for time remaining.
  notification_s notification;
  //1st notification occurs after two loops, but it usually won't print
  //anything because not enough total_time will have passed (first_interval).
  //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
  notification.interval=2;
  //Desired min and max intervals in seconds b/w notifications.
  notification.first_interval=10;
  notification.min_interval=60, notification.max_interval=90;
  //notification.min_interval=30, notification.max_interval=90;
  notification.start_time=-1; notification.current_time=-1;
  notification.previous_time=-1;//Needs to be initialized
  notification.prefix = "Grid xyz";

  notification.starting_index = 0;
  notification.partial_saves_enabled = 0;

  //Used to size notification intervals.
  notification.previous_i=notification.starting_index;
  notification.num_loops = (long long)grid.lat.size()-notification.starting_index;
  //notification.interval will not be allowed to go above this number
  //so that at LEAST a certain number of messages are seen.
  notification.max_notif_interval=(long long)(notification.num_loops/4);

  //Annoyingly, these need to be defined! Argh!
  least_squares_s dummy_ls;
  input_s dummy_input;
  results_s dummy_ar;

  notification.start_time = time(NULL);

  for(i=0;i<(long long)grid.lat.size();i++){

    notify(notification,i,dummy_ls,dummy_input,dummy_ar);

    //Convert grid lat/lon/elev to x,y,z.
    llh2xyz(x,y,z,grid.lat.at(i),grid.lon.at(i),0.0);
    grid.x.at(i)=x;
    grid.y.at(i)=y;
    grid.z.at(i)=z;

  }

  notification.end_time = time(NULL);
  notification.time_total=notification.end_time-notification.start_time;
  cout<<notification.prefix<<" took "
      <<seconds_to_string(notification.time_total)<<endl;
}

void calculate_grid_topography(grid_s &grid, ancillary_s &ancillary){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i;
  grid_s temp_grid;

  //Need to be empty!
  grid.distance_to_land.clear();
  grid.distance_to_ocean.clear();
  grid.elev.clear();
  grid.wcover.clear();
  grid.wdepth.clear();

  //Faster than push_back()!
  grid.distance_to_land.resize(grid.lat.size(),0);
  grid.distance_to_ocean.resize(grid.lat.size(),0);
  grid.elev.resize(grid.lat.size(),0);
  grid.wcover.resize(grid.lat.size(),0);
  grid.wdepth.resize(grid.lat.size(),0);

  //Initialize variables for notification function for time remaining.
  notification_s notification;
  //1st notification occurs after two loops, but it usually won't print
  //anything because not enough total_time will have passed (first_interval).
  //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
  notification.interval=2;
  //Desired min and max intervals in seconds b/w notifications.
  notification.first_interval=10;
  notification.min_interval=60, notification.max_interval=90;
  //notification.min_interval=30, notification.max_interval=90;
  notification.start_time=-1; notification.current_time=-1;
  notification.previous_time=-1;//Needs to be initialized
  notification.prefix = "Grid topography";

  notification.starting_index = 0;
  notification.partial_saves_enabled = 0;

  //Used to size notification intervals.
  notification.previous_i=notification.starting_index;
  notification.num_loops = (long long)grid.lat.size()-notification.starting_index;
  //notification.interval will not be allowed to go above this number
  //so that at LEAST a certain number of messages are seen.
  notification.max_notif_interval=(long long)(notification.num_loops/4);

  //Annoyingly, these need to be defined! Argh!
  least_squares_s dummy_ls;
  input_s dummy_input;
  results_s dummy_ar;

  notification.start_time = time(NULL);

  for(i=0;i<(long long)grid.lat.size();i++){

    notify(notification,i,dummy_ls,dummy_input,dummy_ar);

    //Calc dist to land and ocean, elev of nearest topo pt. and wcover.
    temp_grid = local_topography(grid.lat.at(i),grid.lon.at(i),ancillary);
    grid.distance_to_land.at(i)=temp_grid.distance_to_land.front();
    grid.distance_to_ocean.at(i)=temp_grid.distance_to_ocean.front();
    grid.elev.at(i)=temp_grid.elev.front();
    grid.wcover.at(i)=temp_grid.wcover.front();
    grid.wdepth.at(i)=temp_grid.wdepth.front();

    //Debug.
    if(temp_grid.distance_to_land.front() < temp_grid.distance_to_ocean.front() and temp_grid.elev.front() < 0.0) cout<<"WOAH! temp_grid.distance_to_land.front(): "<<temp_grid.distance_to_land.front()<<" and temp_grid.distance_to_ocean.front(): "<<temp_grid.distance_to_ocean.front()<<" but temp_grid.elev.front(): "<<temp_grid.elev.front()<<endl;
    if(temp_grid.distance_to_land.front() > temp_grid.distance_to_ocean.front() and temp_grid.elev.front() > 0.0) cout<<"WOAH! temp_grid.distance_to_land.front(): "<<temp_grid.distance_to_land.front()<<" and temp_grid.distance_to_ocean.front(): "<<temp_grid.distance_to_ocean.front()<<" but temp_grid.elev.front(): "<<temp_grid.elev.front()<<endl;

  }

  notification.end_time = time(NULL);
  notification.time_total=notification.end_time-notification.start_time;
  cout<<notification.prefix<<" took "
    <<seconds_to_string(notification.time_total)<<endl;
}

void calculate_grid_topography2(grid_s &grid, ancillary_s &ancillary){
  /***********************************************************************
  Purpose: This function uses the new local_topography2() function to
           quickly analyze topo points only in each grid pt's bin. That
           means distance_to_land/ocean are capped at
           grid.options.lat_spacing by fiat, stored as planet circumf.

           At the same time, wcover and wdepth are calculated.
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i;
  grid_s temp_grid;

  //Need to be empty!
  grid.distance_to_land.clear();
  grid.distance_to_ocean.clear();
  grid.elev.clear();
  grid.wcover.clear();
  grid.wdepth.clear();

  //Faster than push_back()!
  grid.distance_to_land.resize(grid.lat.size(),0);
  grid.distance_to_ocean.resize(grid.lat.size(),0);
  grid.elev.resize(grid.lat.size(),0);
  grid.wcover.resize(grid.lat.size(),0);
  grid.wdepth.resize(grid.lat.size(),0);

  //Initialize variables for notification function for time remaining.
  notification_s notification;
  //1st notification occurs after two loops, but it usually won't print
  //anything because not enough total_time will have passed (first_interval).
  //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
  notification.interval=2;
  //Desired min and max intervals in seconds b/w notifications.
  notification.first_interval=10;
  notification.min_interval=60, notification.max_interval=90;
  //notification.min_interval=30, notification.max_interval=90;
  notification.start_time=-1; notification.current_time=-1;
  notification.previous_time=-1;//Needs to be initialized
  notification.prefix = "Grid topography2";

  notification.starting_index = 0;
  notification.partial_saves_enabled = 0;

  //Used to size notification intervals.
  notification.previous_i=notification.starting_index;
  notification.num_loops = (long long)grid.lat.size()-notification.starting_index;
  //notification.interval will not be allowed to go above this number
  //so that at LEAST a certain number of messages are seen.
  notification.max_notif_interval=(long long)(notification.num_loops/4);

  //Annoyingly, these need to be defined! Argh!
  least_squares_s dummy_ls;
  input_s dummy_input;
  results_s dummy_ar;

  notification.start_time = time(NULL);

  for(i=0;i<(long long)grid.lat.size();i++){
    notify(notification,i,dummy_ls,dummy_input,dummy_ar);

    //Calc dist to land/ocean, elev of nearest topo pt. and wcover.
    temp_grid = local_topography2(grid,i,ancillary);
    grid.distance_to_land.at(i)=temp_grid.distance_to_land.front();
    grid.distance_to_ocean.at(i)=temp_grid.distance_to_ocean.front();
    grid.elev.at(i)=temp_grid.elev.front();
    grid.wcover.at(i)=temp_grid.wcover.front();
    grid.wdepth.at(i)=temp_grid.wdepth.front();

    //Debug.
    if(temp_grid.distance_to_land.front() < temp_grid.distance_to_ocean.front() and temp_grid.elev.front() < 0.0) cout<<"WOAH! temp_grid.distance_to_land.front(): "<<temp_grid.distance_to_land.front()<<" and temp_grid.distance_to_ocean.front(): "<<temp_grid.distance_to_ocean.front()<<" but temp_grid.elev.front(): "<<temp_grid.elev.front()<<endl;
    if(temp_grid.distance_to_land.front() > temp_grid.distance_to_ocean.front() and temp_grid.elev.front() > 0.0) cout<<"WOAH! temp_grid.distance_to_land.front(): "<<temp_grid.distance_to_land.front()<<" and temp_grid.distance_to_ocean.front(): "<<temp_grid.distance_to_ocean.front()<<" but temp_grid.elev.front(): "<<temp_grid.elev.front()<<endl;

  }

  notification.end_time = time(NULL);
  notification.time_total=notification.end_time-notification.start_time;
  cout<<notification.prefix<<" took "
    <<seconds_to_string(notification.time_total)<<endl;
}

grid_s local_topography(double current_lat, double current_lon,
                        ancillary_s &ancillary){
  /**********************************************************************
  Purpose: This function finds the nearest land/ocean pts in the ancillary
          structure to (current_lat,current_lon) and returns its distance to
          (current_lat,current_lon) in km.  Whichever of these points is
          closest has its elevation returned as well.  This elevation is
          assumed to be the elevation at (current_lat,current_lon) as long
          as the distance to the closest point is "small enough" - currently
          this function is set to complain if the distance is >29 km.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          double current_lat - latitude of point in question.
          double current_lon - longitude of point in question.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  grid_s output;
  double current_angle;
  double smallest_distance;
  double smallest_land_angle=1000.0,smallest_ocean_angle=1000.0;
  long long k,smallest_land_k=0,smallest_ocean_k=0;

  input_s temp_input, blank_input;

  for(k=0;k<(long long)ancillary.topo.lat.size();k++){
    current_angle = spherical_distance(current_lat,current_lon,
                      ancillary.topo.lat.at(k),ancillary.topo.lon.at(k));
    //Is the k'th topo point on land?
    if(ancillary.topo.elev.at(k) > 0){
      if(current_angle < smallest_land_angle){
        smallest_land_angle = current_angle;
        smallest_land_k = k;
      }
    }
    //If not, it must be in the ocean.
    else{
      if(current_angle < smallest_ocean_angle){
        smallest_ocean_angle = current_angle;
        smallest_ocean_k = k;
      }
    }

    /*
    //Check to see if this topo pt is in mascon's bin.
    //SHOULD PROBABLY THINK VERY LONG AND HARD ABOUT USING DIST2OCEAN
    //TO LABEL A MASCON LAND OR OCEAN BECAUSE OF RESOLUTION ISSUE. RESOLUTION
    //SHOULD BE AUTO-DETERMINED, PROBABLY BY SPOT-CHECKING THE DISTANCE FROM
    //N TOPO PTS TO THE NEAREST OTHER TOPO PT, THEN REPORT AVG/MAX/MIN.
    //THAT WAY THE DISTANCES WOULDN'T HAVE TO BE ZEROED LIKE THEY ARE NOW!
    //BUT STILL, wcover WOULD BE A BETTER WAY TO DETERMINE IF A MASCON
    //IS WATER OR LAND.

    if(is_pt_in_bin(grid, ancillary.topo.lat.at(k), ancillary.topo.lon.at(k), i, newbin) == 1) bin_found=i;
      //If so, use elevation of pt to increment wcover and
      //possibly wdepth if elev < 0.
    }//*/
  }//End of loop though topo.

  //DUMMY VALUES! FIX EVERYTHING MENTIONED ABOVE!
  output.wcover.push_back(1.0);
  output.wdepth.push_back(666);

  //Is (current_lat,current_lon) in the ocean?
  if(smallest_ocean_angle < smallest_land_angle){
    //Calculate distance to the ocean (which MUST be <29km away!)
    smallest_distance = smallest_ocean_angle*planet_avg_radius;
    if(smallest_distance > max_topo_res){
      printf("!!!!WARNING!!! Point at (%4.1f,%5.1f) is %.0f km from topo pt (%4.1f,%5.1f), elev=%.0f m.\n", current_lat, current_lon, smallest_distance, ancillary.topo.lat.at(smallest_ocean_k), ancillary.topo.lon.at(smallest_ocean_k), ancillary.topo.elev.at(smallest_ocean_k)); cout<<endl;
    }
    //Save distance to ocean and depth of ocean at that point.
    output.distance_to_ocean.push_back(smallest_distance);
    output.elev.push_back(ancillary.topo.elev.at(smallest_ocean_k));
    //Save distance to land, but don't bother saving elev at that point.
    smallest_distance = smallest_land_angle*planet_avg_radius;
    output.distance_to_land.push_back(smallest_distance);
  }
  //If not, it must be on land.
  else{
    //Calculate distance to the land (which MUST be <29km away!)
    smallest_distance = smallest_land_angle*planet_avg_radius;
    if(smallest_distance > max_topo_res){
      printf("!!!!WARNING!!! Point at (%4.1f,%5.1f) is %.0f km from topo pt (%4.1f,%5.1f), elev=%.0f m.\n", current_lat, current_lon, smallest_distance, ancillary.topo.lat.at(smallest_land_k), ancillary.topo.lon.at(smallest_land_k), ancillary.topo.elev.at(smallest_land_k)); cout<<endl;
    }
    //Save distance to land and elev of land at that point.
    output.distance_to_land.push_back(smallest_distance);
    output.elev.push_back(ancillary.topo.elev.at(smallest_land_k));
    //Save distance to ocean, but don't bother saving depth at that point.
    smallest_distance = smallest_ocean_angle*planet_avg_radius;
    output.distance_to_ocean.push_back(smallest_distance);
  }
  return output;
}

grid_s local_topography2(grid_s &grid, long long i, ancillary_s &ancillary){
  /**********************************************************************
  Purpose: This function finds the nearest land/ocean pts in the ancillary
          structure to pt "i" in grid and returns its distance to pt "i"
          in km. Whichever of these points is closest has its elevation
          returned as well. This elevation is assumed to be the elevation
          at pt "i" as long as the distance to the closest point is "small
          enough" as determined by max_topo_res in definitions.hpp.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          long long i - index of grid point in question.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  grid_s output;
  double current_angle;
  double smallest_distance;
  double init_angle=1000.0;
  double smallest_land_angle=init_angle,smallest_ocean_angle=init_angle;
  long long k,smallest_land_k=0,smallest_ocean_k=0;

  double current_lat = grid.lat.at(i),  current_lon = grid.lon.at(i);

  //Number of points in current bin.
  long long num_pts=0,land_pts=0,water_pts=0;
  double depth=0.0;

  if(ancillary.topo.format == 0){//List of lats,lons,elevs.
    //Just so bin boundaries are defined.
    int newbin=1; is_pt_in_bin(grid,ancillary.topo.lat.front(),ancillary.topo.lon.front(),i,newbin);

    //New version of topo loop only calcs dist to pts in bin!
    for(k=0;k<(long long)ancillary.topo.lat.size();k++){
      //First, check to see if lat is "close enough" for better speed.
      if(ancillary.topo.lat.at(k)<grid.maxlat and ancillary.topo.lat.at(k)>=grid.minlat){
        if(is_pt_in_bin(grid, ancillary.topo.lat.at(k), ancillary.topo.lon.at(k), i, newbin) == 1){

          current_angle = spherical_distance(current_lat,current_lon,
                            ancillary.topo.lat.at(k),
                            ancillary.topo.lon.at(k));

          num_pts++;

          //Is the k'th topo point on land?
          if(ancillary.topo.elev.at(k) > 0){
            land_pts++;
            if(current_angle < smallest_land_angle){
              smallest_land_angle = current_angle;
              smallest_land_k = k;
            }
          }
          //If not, it must be in the ocean.
          else{
            water_pts++; depth-=ancillary.topo.elev.at(k);
            if(current_angle < smallest_ocean_angle){
              smallest_ocean_angle = current_angle;
              smallest_ocean_k = k;
            }
          }
        }//End of "if in bin."
      }//End of "if ancillary lat is close enough to grid's lat."
    }//End of loop though topo.

    //If no topo pts in bin, need to expand search.
    //REALLY BAD STYLE- COPIED DIRECTLY FROM ABOVE!!!
    if(num_pts==0){
      //cout<<"Expanding topo search for grid pt i="<<i<<endl;
      grid_s temp_grid=grid;//So halfwidths can be modified.

      //So other pts can expand to NP.
      if(fabs(grid.lat.at(i)) != 90.0) temp_grid.options.cap_size=0.0;

      //Loop until at least 1 pt found.
      while(num_pts==0){

        //Expand bin.
        if(fabs(grid.lat.at(i)) != 90.0){
          temp_grid.lat_halfwidth.at(i) *= 2;
          temp_grid.lon_halfwidth.at(i) *= 2;
          //Probably a good idea to keep these statements on, because
          //the out-of-bounds bin boundaries will be a problem someday!
          //NO, IT'S *WAY* TOO MUCH INFORMATION!
          //cout<<"lat halfwidth: "<<temp_grid.lat_halfwidth.at(i)<<endl;
        }
        else{
          temp_grid.lat_halfwidth.at(i) *= 2;
          //Don't adjust lon_halfwidth, because it's already at maximum.
          //cout<<"lat halfwidth: "<<temp_grid.lat_halfwidth.at(i)<<endl;
        }

        //Just so bin boundaries are defined.
        newbin=1; is_pt_in_bin(temp_grid,ancillary.topo.lat.front(),ancillary.topo.lon.front(),i,newbin);

        //New version of topo loop only calcs dist to pts in bin!
        for(k=0;k<(long long)ancillary.topo.lat.size();k++){
          //First, check to see if lat is "close enough" for better speed.
          if(ancillary.topo.lat.at(k)<temp_grid.maxlat and ancillary.topo.lat.at(k)>=temp_grid.minlat){
            if(is_pt_in_bin(temp_grid, ancillary.topo.lat.at(k), ancillary.topo.lon.at(k), i, newbin) == 1){

              current_angle = spherical_distance(current_lat,current_lon,
                                ancillary.topo.lat.at(k),
                                ancillary.topo.lon.at(k));

              num_pts++;

              //Is the k'th topo point on land?
              if(ancillary.topo.elev.at(k) > 0){
                land_pts++;
                if(current_angle < smallest_land_angle){
                  smallest_land_angle = current_angle;
                  smallest_land_k = k;
                }
              }
              //If not, it must be in the ocean.
              else{
                water_pts++; depth-=ancillary.topo.elev.at(k);
                if(current_angle < smallest_ocean_angle){
                  smallest_ocean_angle = current_angle;
                  smallest_ocean_k = k;
                }
              }
            }//End of "if in bin."
          }//End of "if lat is close enough."
        }//End of loop though topo.
      }
      //Now that at least 1 pt has been found, see if the closest is land/ocean
      if(smallest_land_angle < smallest_ocean_angle){
        land_pts=1;
        water_pts=0;//These points are outside bin! They're FAKE!
      }
      else{
        land_pts=0;
        depth/=water_pts;//Need to divide depth by ACTUAL water_pts first.
        water_pts=1;
      }
    }

    if(water_pts==0){
      output.wcover.push_back(0.0);
      output.wdepth.push_back(0.0);
    }
    else if(land_pts==0){
      output.wcover.push_back(1.0);
      depth/=water_pts;
      output.wdepth.push_back(depth);
    }
    else{
      output.wcover.push_back((double)water_pts/(double)num_pts);
      depth/=water_pts;
      output.wdepth.push_back(depth);
    }

    //Is (current_lat,current_lon) in the ocean?
    if(smallest_ocean_angle < smallest_land_angle){
      //Calculate distance to the ocean (which MUST be <29km away!)
      smallest_distance = smallest_ocean_angle*planet_avg_radius;
      if(smallest_distance > max_topo_res){
        printf("!!!!WARNING!!! Point at (%4.1f,%5.1f) is %.0f km from topo pt (%4.1f,%5.1f), elev=%.0f m.\n", current_lat, current_lon, smallest_distance, ancillary.topo.lat.at(smallest_ocean_k), ancillary.topo.lon.at(smallest_ocean_k), ancillary.topo.elev.at(smallest_ocean_k)); cout<<endl;
      }
      //Save distance to ocean and depth of ocean at that point.
      output.distance_to_ocean.push_back(smallest_distance);
      output.elev.push_back(ancillary.topo.elev.at(smallest_ocean_k));

      //Save distance to land, but don't bother saving elev at that point.
      //If smallest_land_angle is still init_angle, land is outside this bin.
      //Just set that distance to pi*planet_avg_radius and hope algorithms won't fail!
      if(smallest_land_angle==init_angle) output.distance_to_land.push_back(pi*planet_avg_radius);
      else output.distance_to_land.push_back(smallest_land_angle*planet_avg_radius);

    }
    //If not, it must be on land.
    else{
      //Calculate distance to the land (which MUST be <29km away!)
      smallest_distance = smallest_land_angle*planet_avg_radius;
      if(smallest_distance > max_topo_res){
        printf("!!!!WARNING!!! Point at (%4.1f,%5.1f) is %.0f km from topo pt (%4.1f,%5.1f), elev=%.0f m.\n", current_lat, current_lon, smallest_distance, ancillary.topo.lat.at(smallest_land_k), ancillary.topo.lon.at(smallest_land_k), ancillary.topo.elev.at(smallest_land_k)); cout<<endl;
      }
      //Save distance to land and elev of land at that point.
      output.distance_to_land.push_back(smallest_distance);
      output.elev.push_back(ancillary.topo.elev.at(smallest_land_k));

      //Save distance to ocean, but don't bother saving depth at that point.
      //If smallest_ocean_angle is still init_angle, land is outside this bin.
      //Just set that distance to pi*planet_avg_radius and hope algorithms won't fail!
      if(smallest_ocean_angle==init_angle) output.distance_to_ocean.push_back(pi*planet_avg_radius);
      else output.distance_to_ocean.push_back(smallest_ocean_angle*planet_avg_radius);
    }
  }
  ////////////////////////////////////////////////////////////////////////////////////////
  //2D code starts here.
  ////////////////////////////////////////////////////////////////////////////////////////
  else if(ancillary.topo.format == 1){//Elevs in 2D equal-grid grid.
    long long j,smallest_land_j=0,smallest_ocean_j=0;

    //Just so bin boundaries are defined.
    int newbin=1; is_pt_in_bin(grid,ancillary.topo.hr_lats.front(),ancillary.topo.hr_lons.front(),i,newbin);

    //This topo loop only calcs dist to pts in bin, and cycles through 2D grid.
    for(k=0;k<(long long)ancillary.topo.hr_lats.size();k++){
      //First, check to see if lat is "close enough" for better speed.
      if(ancillary.topo.hr_lats.at(k)<grid.maxlat and ancillary.topo.hr_lats.at(k)>=grid.minlat){
        //Now loop through longitudes.
        for(j=0;j<(long long)ancillary.topo.hr_lons.size();j++){
          if(is_pt_in_bin(grid, ancillary.topo.hr_lats.at(k), ancillary.topo.hr_lons.at(j), i, newbin) == 1){

            current_angle = spherical_distance(current_lat,current_lon,
                              ancillary.topo.hr_lats.at(k),
                              ancillary.topo.hr_lons.at(j));

            num_pts++;

            //Is the k'th topo point on land?
            if(ancillary.topo.hr_elev.at(k).at(j) > 0){
              land_pts++;
              if(current_angle < smallest_land_angle){
                smallest_land_angle = current_angle;
                smallest_land_k = k;
                smallest_land_j = j;
              }
            }
            //If not, it must be in the ocean.
            else{
              water_pts++; depth-=ancillary.topo.hr_elev.at(k).at(j);
              if(current_angle < smallest_ocean_angle){
                smallest_ocean_angle = current_angle;
                smallest_ocean_k = k;
                smallest_ocean_j = j;
              }
            }
          }//End of "if in bin."
        }//End of loop through longitudes.
      }//End of "if ancillary lat is close enough to grid's lat."
    }//End of loop though topo lats.

    //If no topo pts in bin, need to expand search.
    //REALLY BAD STYLE- COPIED DIRECTLY FROM ABOVE!!!
    if(num_pts==0){
      //cout<<"Expanding topo search for grid pt i="<<i<<endl;
      grid_s temp_grid=grid;//So halfwidths can be modified.

      //So other pts can expand to NP.
      if(fabs(grid.lat.at(i)) != 90.0) temp_grid.options.cap_size=0.0;

      //Loop until at least 1 pt found.
      while(num_pts==0){

        //Expand bin.
        if(fabs(grid.lat.at(i)) != 90.0){
          temp_grid.lat_halfwidth.at(i) *= 2;
          temp_grid.lon_halfwidth.at(i) *= 2;
          //Probably a good idea to keep these statements on, because
          //the out-of-bounds bin boundaries will be a problem someday!
          //NO, IT'S *WAY* TOO MUCH INFORMATION!
          //cout<<"lat halfwidth: "<<temp_grid.lat_halfwidth.at(i)<<endl;
        }
        else{
          temp_grid.lat_halfwidth.at(i) *= 2;
          //Don't adjust lon_halfwidth, because it's already at maximum.
          //cout<<"lat halfwidth: "<<temp_grid.lat_halfwidth.at(i)<<endl;
        }

        //Just so bin boundaries are defined.
        newbin=1; is_pt_in_bin(temp_grid,ancillary.topo.hr_lats.front(),ancillary.topo.hr_lons.front(),i,newbin);

        //New version of topo loop only calcs dist to pts in bin!
        for(k=0;k<(long long)ancillary.topo.hr_lats.size();k++){
          //First, check to see if lat is "close enough" for better speed.
          if(ancillary.topo.hr_lats.at(k)<temp_grid.maxlat and ancillary.topo.hr_lats.at(k)>=temp_grid.minlat){
            //Now loop through longitudes.
            for(j=0;j<(long long)ancillary.topo.hr_lons.size();j++){
              if(is_pt_in_bin(temp_grid, ancillary.topo.hr_lats.at(k), ancillary.topo.hr_lons.at(j), i, newbin) == 1){

                current_angle = spherical_distance(current_lat,current_lon,
                                  ancillary.topo.hr_lats.at(k),
                                  ancillary.topo.hr_lons.at(j));

                num_pts++;

                //Is the k'th topo point on land?
                if(ancillary.topo.hr_elev.at(k).at(j) > 0){
                  land_pts++;
                  if(current_angle < smallest_land_angle){
                    smallest_land_angle = current_angle;
                    smallest_land_k = k;
                    smallest_land_j = j;
                  }
                }
                //If not, it must be in the ocean.
                else{
                  water_pts++; depth-=ancillary.topo.hr_elev.at(k).at(j);
                  if(current_angle < smallest_ocean_angle){
                    smallest_ocean_angle = current_angle;
                    smallest_ocean_k = k;
                    smallest_ocean_j = j;
                  }
                }
              }//End of "if in bin."
            }//End of loop through lons.
          }//End of "if lat is close enough."
        }//End of loop though topo lats.
      }
      //Now that at least 1 pt has been found, see if the closest is land/ocean
      if(smallest_land_angle < smallest_ocean_angle){
        land_pts=1;
        water_pts=0;//These points are outside bin! They're FAKE!
      }
      else{
        land_pts=0;
        depth/=water_pts;//Need to divide depth by ACTUAL water_pts first.
        water_pts=1;
      }
    }

    if(water_pts==0){
      output.wcover.push_back(0.0);
      output.wdepth.push_back(0.0);
    }
    else if(land_pts==0){
      output.wcover.push_back(1.0);
      depth/=water_pts;
      output.wdepth.push_back(depth);
    }
    else{
      output.wcover.push_back((double)water_pts/(double)num_pts);
      depth/=water_pts;
      output.wdepth.push_back(depth);
    }

    //Is (current_lat,current_lon) in the ocean?
    if(smallest_ocean_angle < smallest_land_angle){
      //Calculate distance to the ocean (which MUST be <29km away!)
      smallest_distance = smallest_ocean_angle*planet_avg_radius;
      if(smallest_distance > max_topo_res){
        printf("!!!!WARNING!!! Point at (%4.1f,%5.1f) is %.0f km from topo pt (%4.1f,%5.1f), elev=%d m.\n", current_lat, current_lon, smallest_distance, ancillary.topo.hr_lats.at(smallest_ocean_k), ancillary.topo.hr_lons.at(smallest_ocean_j), ancillary.topo.hr_elev.at(smallest_ocean_k).at(smallest_ocean_j)); cout<<endl;
      }
      //Save distance to ocean and depth of ocean at that point.
      output.distance_to_ocean.push_back(smallest_distance);
      output.elev.push_back(ancillary.topo.hr_elev.at(smallest_ocean_k).at(smallest_ocean_j));

      //Save distance to land, but don't bother saving elev at that point.
      //If smallest_land_angle is still init_angle, land is outside this bin.
      //Just set that distance to pi*planet_avg_radius and hope algorithms won't fail!
      if(smallest_land_angle==init_angle) output.distance_to_land.push_back(pi*planet_avg_radius);
      else output.distance_to_land.push_back(smallest_land_angle*planet_avg_radius);

    }
    //If not, it must be on land.
    else{
      //Calculate distance to the land (which MUST be <29km away!)
      smallest_distance = smallest_land_angle*planet_avg_radius;
      if(smallest_distance > max_topo_res){
        printf("!!!!WARNING!!! Point at (%4.1f,%5.1f) is %.0f km from topo pt (%4.1f,%5.1f), elev=%d m.\n", current_lat, current_lon, smallest_distance, ancillary.topo.hr_lats.at(smallest_land_k), ancillary.topo.hr_lons.at(smallest_land_j), ancillary.topo.hr_elev.at(smallest_land_k).at(smallest_land_j)); cout<<endl;
      }
      //Save distance to land and elev of land at that point.
      output.distance_to_land.push_back(smallest_distance);
      output.elev.push_back(ancillary.topo.hr_elev.at(smallest_land_k).at(smallest_land_j));

      //Save distance to ocean, but don't bother saving depth at that point.
      //If smallest_ocean_angle is still init_angle, land is outside this bin.
      //Just set that distance to pi*planet_avg_radius and hope algorithms won't fail!
      if(smallest_ocean_angle==init_angle) output.distance_to_ocean.push_back(pi*planet_avg_radius);
      else output.distance_to_ocean.push_back(smallest_ocean_angle*planet_avg_radius);
    }
  }
  else cout<<"Don't recognize topo format "<<ancillary.topo.format<<endl;

  //printf("Pt (%4.1f,%5.1f) has %.2f%% wc, %.0fm depth, %lld topo pts in bin.", current_lat, current_lon, output.wcover.front()*100.0, output.wdepth.front(),num_pts); cout<<endl;

  return output;
}

void grid_cm2kg(grid_s &grid){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i,j;

  grid.area.clear();
  grid.area.resize((long long)grid.lat.size());
  cout<<"Calculating areas using area_type "<<grid.options.area_type<<"..."<<endl;
  switch(grid.options.area_type){
    default: cout<<"!!!!WARNING!!!!!! Grid area type "<<grid.options.area_type<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////
    case 0:{//0 - Uses lat/lon_ halfwidth for approx. rectangular area.
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) == 90.0){
          grid.area.at(i) = pi*pow(planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(i)-grid.options.cap_size*km2deg,grid.lon.at(i))*1000.0,2);
          //cout<<"North pole bin has index "<<i<<" and has area: "<<grid.area.at(i)<<" m^2."<<endl;
        }
        else if(grid.lat.at(i) == -90.0){
          grid.area.at(i) = pi*pow(planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(i)+grid.options.cap_size*km2deg,grid.lon.at(i))*1000.0,2);
          //cout<<"South pole bin has index "<<i<<" and has area: "<<grid.area.at(i)<<" m^2."<<endl;
        }
        else{//Any other grid point.
          grid.area.at(i) = (planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(i)-2*grid.lat_halfwidth.at(i),grid.lon.at(i))*1000.0)*(planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(i),grid.lon.at(i)-2*grid.lon_halfwidth.at(i))*1000.0);
          //cout<<"grid.area.at("<<i<<") = "<<grid.area.at(i)<<" m^2."<<endl;
          if(grid.lat.at(i) < 50.0) cout<<"!!!WARNING!!! GRID AREA TYPE 0 MAY ENOUNTER PROBLEMS WHEN SOUTHERN HEMISPHERE IS INCLUDED BECAUSE AREA IS CALCULATED BY SUBTRACTING 2*LAT_HALFWIDTH FROM LAT OF GRID POINT."<<endl;
        }
      }//End of loop through grid points.
      break;
    }
    //////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////
    case 1:{//1 - Uses results.options.disk_radius to calc circular areas.
             /*BAD IDEA!  Doesn't completely cover the earth!
      cout<<"Disk radius used for cm2kg conversion: "<<results.options.disk_radius<<" km."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        grid.area.at(i) = pi*pow(results.options.disk_radius,2)*1000.0*1000.0;
        //cout<<"grid.area.at("<<i<<") = "<<grid.area.at(i)<<" m^2."<<endl;
      }//*/
      break;
    }
    //////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////
    case 2:{//Accounts for curvature of the spherical earth, assumes
             //grid is composed of at least one pole cap and all other
             //grid points are layed out in rows, with each grid point
             //at a particular lat having the exact same area.

      //Need to know how many grid points are at each lat.
      vector<long long> number_of_points_at_different_lats;
      number_of_points_at_different_lats.resize(grid.different_lats.size(),0);
      //Also need to know which diff_lat index matches lat of each grid pt.
      vector<long long> different_lats_indices;
      different_lats_indices.resize(grid.lat.size(),0);

      //Check: how many times was the area recorded for each grid point?
      vector<long long> numwrites;
      numwrites.resize(grid.lat.size(),0);

      double current_area;
      double larger_phi,smaller_phi;//rads, edges of bin measrd from pole.

      //Treat poles completely differently than other points.
      //This loop also checks to see how many points are at each latitude.
      for(i=0;i<(long long)grid.lat.size();i++){
        //Both caps can be handled the same way.
        if(fabs(grid.lat.at(i) >= 90.0)){
          //lat_hw = cap_size.
          larger_phi = (grid.lat_halfwidth.at(i))*deg2rad;
          //(1E6-km^2 to m^2), smaller_phi=0.
          grid.area.at(i) = (pow(planet_avg_radius,2)*1E6)*twoPi*(cos(0.0)-cos(larger_phi));
          //Note that area was recorded for this grid point.
          numwrites.at(i)++;

          for(j=0;j<(long long)grid.different_lats.size();j++){
            //Is this the diff_lat that matches this pole?
            if(fabs(grid.lat.at(i)-grid.different_lats.at(j))<tolerance){
              //Note that this grid pnt is at the j'th index in diff_lats.
              different_lats_indices.at(i)=j;
              //Increment the number of grid points at the j'th diff_lats.
              number_of_points_at_different_lats.at(j)++;
            }//If this is the j'th diff_lat that matches the pole.
          }//Loop through diff_lat.
        }
        else{//Keep track of how many points are at each lat.

          //Search diff_lats to see which j'th lat index matches the lat of
          //this i'th grid point.
          int already_found_matching_lat=0;
          for(j=0;j<(long long)grid.different_lats.size();j++){

            //Is this the diff_lat that matches the i'th grid point's lat?
            if(fabs(grid.lat.at(i)-grid.different_lats.at(j))<tolerance){
              if(already_found_matching_lat != 0)
                cout<<"!!!WARNING!!! area code seems to have matched the "<<i<<"th grid point with "<<already_found_matching_lat+1<<" different_lats."<<endl;
              already_found_matching_lat++;

              //Note that this grid point is at the j'th index in diff_lats.
              different_lats_indices.at(i)=j;
              //Increment the number of grid points at the j'th diff_lats.
              number_of_points_at_different_lats.at(j)++;

            }//If the i'th grid point's lat = j'th different_lat.
          }//Loop through diff_lat.
        }//If this point isn't a polar point.
      }//End of loop to look for poles and number of points at each lat.

      //Treat other points as identical in area if at same lat.
      for(j=0;j<(long long)grid.different_lats.size();j++){

        for(i=0;i<(long long)grid.lat.size();i++){

          int already_found_matching_lat=0;
          //Only save area for this i'th grid point if it's got the same
          //latitude as the j'th diff_lat, and as long as it isn't a pole.
          if(grid.lat.at(i) == grid.different_lats.at(j) and fabs(grid.lat.at(i)) < 90.0){

            if(already_found_matching_lat==0){

              larger_phi = (90.0-fabs(grid.lat.at(i))+grid.lat_halfwidth.at(i))*deg2rad;
              smaller_phi = (90.0-fabs(grid.lat.at(i))-grid.lat_halfwidth.at(i))*deg2rad;
              //Just checkin'
              if(larger_phi<0 or smaller_phi<0 or smaller_phi>=larger_phi)
                cout<<"!!!!!WARNING!!! larger_phi = "<<larger_phi<<" and smaller_phi = "<<smaller_phi<<" at grid.lat["<<i<<"] = "<<grid.lat.at(i)<<" which is the same as grid.diff_lats["<<j<<"] = "<<grid.different_lats.at(j)<<endl;
              //Each grid pt at this lat has this area: (1E6-km^2 to m^2)
              current_area = (pow(planet_avg_radius,2)*1E6)*twoPi*(cos(smaller_phi)-cos(larger_phi))/number_of_points_at_different_lats.at(j);
              //Record area for this grid point.
              grid.area.at(i) = current_area;
              //Note that area was recorded for this grid point.
              numwrites.at(i)++;
            }//End of "need to initialize area".
            else{
              //Record area for this grid point.
              grid.area.at(i) = current_area;
              //Note that area was recorded for this grid point.
              numwrites.at(i)++;
            }//End of "don't need to initialize area".
          }//End of "this grid.lat = current_lat".
        }//End of loop through grid points.
      }//End of loop through different lats.

      //Check: make sure each grid point only had its area recorded once.
      for(i=0;i<(long long)grid.lat.size();i++){
        //cout<<"numwrites["<<i<<"] = "<<numwrites.at(i)<<endl;
        if(numwrites.at(i) != 1)
          cout<<"!!!WARNING!!! numwrites["<<i<<"] = "<<numwrites.at(i)<<endl;
      }

      //CHECK THAT SUM OF NUM OF POINTS AT DIFF LATS EQUAL GRID.lat.size
      long long templong2=0;
      for(j=0;j<(long long)number_of_points_at_different_lats.size();j++)
        templong2+=number_of_points_at_different_lats.at(j);
      if((long long)grid.lat.size() != templong2)
        cout<<"!!!!WARNING!!!! SHOULDN'T EVER SEE THIS!!! FGTYHE6g4k"<<endl;

      //cout<<"keep track of area lost/dbl-counted in between areas of all points at the same diff_lat by saving old phis- save in vector and present stats at the end?  Be careful about the swaths right next to the equator when I make the entire global grid!  Also be careful to not just average the overlaps- keep track of the positive and negative discrepencies separately (and together too to see how well it averages out)."<<endl;
      break;
    }
    //////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////
    case 3:{
      latlon_s rawfes;
      model_s model;
      model.options.type=1;//Pretend to be FES.
      define_model_titles_and_periods(model);
      string file = gaiafolder();
      file.append(model.options.basefolder);
      file.append(model.options.filenames.front());
      rawfes.filename = file;
      rawfes.discard_redundant_values = 1;
      read_rawfes(rawfes);
      match_fes_to_grid(rawfes,grid);
      sum_fes_areas_in_each_mascon(grid);
      break;
    }
  }//End of switch-case.

  //Now check that the areas make sense.

  //Used to report the max,min,avg areas.
  double north_pole_area=0,south_pole_area=0, max_area, min_area, avg_area, total_area;
  long long index_for_min_area, index_for_max_area;

  //Only bother if there's more than 1 point!
  if(grid.lat.size()>1){

    //Initializations for each type of maximization or averaging or indexing:
    min_area = 1E30*pow(planet_avg_radius,2);//Just to be sure!
    max_area = 0.0;
    avg_area = 0.0;
    index_for_min_area=-1;
    index_for_max_area=-1;
    for(i=0;i<(long long)grid.lat.size();i++){
      //cout<<"grid.area.at("<<i<<") = "<<grid.area.at(i)<<endl;
      avg_area += grid.area.at(i);

      if(grid.lat.at(i) == 90.0)
        north_pole_area = grid.area.at(i);
      else if(grid.lat.at(i) == -90.0)
        south_pole_area = grid.area.at(i);
      //Note that the next elses mean that poles aren't eligible for max/min!
      else if(grid.area.at(i)<min_area){
        min_area=grid.area.at(i);
        index_for_min_area=i;
      }
      else if(grid.area.at(i)>max_area){
        max_area=grid.area.at(i);
        index_for_max_area=i;
      }
    }//End of loop through main grid.

    total_area = avg_area;
    avg_area /= (double)grid.lat.size();

    if(north_pole_area>0) cout<<"North pole area = "<<fixed<<setprecision(2)<<setw(7)<<north_pole_area/1E6<<" km^2."<<endl;
    if(south_pole_area>0) cout<<"South pole area = "<<fixed<<setprecision(2)<<setw(7)<<south_pole_area/1E6<<" km^2."<<endl;

    cout<<"Average (including poles) area = "<<fixed<<setprecision(2)<<setw(7)<<avg_area/1E6<<" km^2."<<endl;
    cout<<"Max (not including poles) area = "<<fixed<<setprecision(2)<<setw(7)<<max_area/1E6<<" km^2 at grid.lat["<<index_for_max_area<<"] = "<<grid.lat.at(index_for_max_area)<<" and grid.lon["<<index_for_max_area<<"] = "<<grid.lon.at(index_for_max_area)<<"."<<endl;
    cout<<"Min (not including poles) area = "<<fixed<<setprecision(2)<<setw(7)<<min_area/1E6<<" km^2 at grid.lat["<<index_for_min_area<<"] = "<<grid.lat.at(index_for_min_area)<<" and grid.lon["<<index_for_min_area<<"] = "<<grid.lon.at(index_for_min_area)<<"."<<endl;
    cout<<"(max_area-min_area)/avg_area (weird because max/min don't include poles but avg does!) = "<<fixed<<setprecision(5)<<setw(8)<<(max_area-min_area)/avg_area<<endl;
    cout<<"Sum of areas = "<<fixed<<setprecision(2)<<setw(7)<<total_area/1E6<<" km^2."<<endl;

    if(grid.options.type==1 or grid.options.type==2 or grid.options.type==3 or grid.options.type==4 or grid.options.type==10 or grid.options.type==11)//Doesn't need /1E6 because I removed 1E6 instead.
      cout<<"Spherical surface area north of "<<grid.options.boundaries[0]<<" N latitude is: "<<fixed<<setprecision(2)<<setw(7)<<pow(planet_avg_radius,2)*twoPi*(cos(0.0)-cos((90-grid.options.boundaries[0])*deg2rad))<<" km^2."<<endl;
    else cout<<"!!!WARNING!! HAVEN'T WRITTEN THIS YET!! 89FREJK4"<<endl;

  }

  //Now, and only now, are densities (kg/m^3) of water calculated.
  grid.density.resize((long long)grid.lat.size());
  grid.cm2kg.resize((long long)grid.lat.size());
  for(i=0;i<(long long)grid.lat.size();i++){
    grid.density.at(i) = sea_density;
    grid.cm2kg.at(i) = grid.area.at(i)*grid.density.at(i)/100.0;//m -> cm
  }//End of loop through grid points.

  cout<<"Finished calculating areas and cm2kg factors."<<endl;
}

void autosize_grid_cap(grid_s &grid){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  double cap_area, nearby_area, current_lat, min_lat, max_lat, min_lon, max_lon, current_lon_halfwidth, current_lat_halfwidth, larger_phi, smaller_phi;
  long long jm,j,im;
  grid_s autogrid;

  autogrid.options=grid.options;

  if(grid.options.type != 2 and grid.options.type != 4 and grid.options.type != 10 and grid.options.type != 11) cout<<"!!!WARNING!!! CODE WAS COPIED FROM DEFINE_GRID() TYPE 2 EQUAL AREA CODE!  ANY OTHER TYPE SUCH AS 1 WON'T WORK, AND GLOBAL TYPES MAY REQUIRE A SEPARATE SET OF STATEMENTS FOR SOUTHERN HEMISPHERE?"<<endl;
  if(grid.options.area_type != 2) cout<<"!!!WARNING!!! CODE WAS COPIED FROM DEFINE_GRID() AREA_TYPE 2 AREA CODE!  ANY OTHER TYPE WON'T WORK!"<<endl;

  int converged=0;//Used to control outer iteration loop.
  long long l=0;//Tracks how many times outer while loop has run.
  double spacing_jitter=1E-2;//Resolution of spacings in cache file.
  //Iterate the lat/lon_spacings around the specified value until
  //the cap_size iteration converges.
  while(converged == 0){
    l++;

    //Need to reset these on each outer loop.
    autogrid.options.lat_spacing = autogrid.options.original_lat_spacing;
    autogrid.options.lon_spacing = autogrid.options.original_lon_spacing;

    //Now jitter the lat_spacing (and lon_spacing if they're identical)
    if(l > 1){//Only jitter if this isn't the first loop.
      if(grid.options.original_lat_spacing == grid.options.original_lon_spacing){
        //If l is even, divide l by 2 and multiply by spacing_jitter,
        //then subtract from orig_lat/lon_spacing to get lat/lon_spacing.
        if(l % 2 == 0){
          autogrid.options.lat_spacing -= (double)(l/2)*spacing_jitter;
          autogrid.options.lon_spacing -= (double)(l/2)*spacing_jitter;
        }
        //Otherwise, odd l values are incremented, divided by 2, then ADDED.
        else{
          autogrid.options.lat_spacing += (double)((l+1)/2)*spacing_jitter;
          autogrid.options.lon_spacing += (double)((l+1)/2)*spacing_jitter;
        }
      }//End of "if orig lat/lon spacings are identical".
      else{
        //If l is even, divide l by 2 and multiply by spacing_jitter,
        //then subtract from orig_lat/lon_spacing to get lat/lon_spacing.
        if(l % 2 == 0)
          autogrid.options.lat_spacing -= (double)(l/2)*spacing_jitter;
        //Otherwise, odd l values are incremented, divided by 2, then ADDED.
        else
          autogrid.options.lat_spacing += (double)((l+1)/2)*spacing_jitter;
      }//End of "if orig lat/lon spacings are NOT identical."
    }

    //cout<<endl<<"Outer iteration loop #"<<l<<" has changed lat_spacing from "<<fixed<<setprecision(2)<<setw(7)<<autogrid.options.original_lat_spacing<<" to "<<autogrid.options.lat_spacing<<endl<<endl;

    //This is 1st guess based on lat_spacing.
    //0.5 factor makes the area sorta close...
    autogrid.options.cap_size = 0.5*autogrid.options.lat_spacing;

    //Need to initialize the areas, the cap_area is actually correct but
    //that doesn't matter because it will be reset quickly in the while
    //loop- the nearby_area is more complicated so it's just set to 0.
    cap_area = (pow(planet_avg_radius,2)*1E6)*twoPi*(cos(0.0)-cos(autogrid.options.cap_size/planet_avg_radius));
    nearby_area=0.0;//So that the while loop is entered.
    long long areacounter=0, max_iterations=20;
    while(fabs(cap_area-nearby_area)/cap_area>high_tolerance and areacounter<max_iterations){

      //Only modify cap_size if at least one loop has been made.
      if(areacounter>0) autogrid.options.cap_size *= sqrt(nearby_area/cap_area);
      areacounter++;
      //cout<<"For iteration "<<areacounter<<", grid.options.cap_size = "<<autogrid.options.cap_size<<" km."<<endl;

      //This is easy.
      cap_area = (pow(planet_avg_radius,2)*1E6)*twoPi*(cos(0.0)-cos(autogrid.options.cap_size/planet_avg_radius));

      //Nearby area is harder...
      current_lat_halfwidth = autogrid.options.lat_spacing*km2deg/2.0;
      //Define the extent of the edges of each bin.
      if(autogrid.options.boundaries[1] != 90.0) cout <<"!!!WARNING: Grid cap autosizing won't work correctly unless boundaries[1] is set to 90.0, because of the cap!"<<endl;
      min_lat = autogrid.options.boundaries[0];
      max_lat = autogrid.options.boundaries[1] - autogrid.options.cap_size*km2deg;
      jm = (long long)((max_lat-min_lat)/(2*current_lat_halfwidth));
      //This next line rounds the halfwisdth to the correct value
      //so that all space is filled- no gaps remain like before.
      if(jm>0) current_lat_halfwidth = (max_lat-min_lat)/jm/2;
      else current_lat_halfwidth = 0.0;
      //Now that the halfwidth has been defined, apply it to the
      //boundaries of the grid, in terms of the centers of the bins.
      min_lat = autogrid.options.boundaries[0] + current_lat_halfwidth;
      max_lat = autogrid.options.boundaries[1] - autogrid.options.cap_size*km2deg - current_lat_halfwidth;
      //"j" used to loop thru lats, just one line's important here though.
      j=0;
      current_lat = max_lat-j*2*current_lat_halfwidth;
      //min_lon and max_lon need to be recalculated at each latitude
      //because lon_halfwidth is different at different latitudes.
      //Using cos(lat) b/c sin(lat) would only work for colatitude.
      current_lon_halfwidth = autogrid.options.lon_spacing*km2deg/2.0/cos(deg2rad*current_lat);
      //Define the extent of the edges of each bin.
      min_lon = autogrid.options.boundaries[2];
      max_lon = autogrid.options.boundaries[3];
      im = (long long)((max_lon-min_lon)/(2*current_lon_halfwidth));
      //cout<<"Nearby areas are at lat "<<current_lat<<", of which there are "<<im<<endl;
      larger_phi = (90.0-(current_lat-current_lat_halfwidth))*deg2rad;
      smaller_phi = (90.0-(current_lat+current_lat_halfwidth))*deg2rad;
      //Just checkin'
      if(larger_phi<0 or smaller_phi<0 or smaller_phi>=larger_phi)
        cout<<"!!!!!WARNING!!! larger_phi = "<<larger_phi<<" and smaller_phi = "<<smaller_phi<<endl;

      nearby_area = (pow(planet_avg_radius,2)*1E6)*twoPi*(cos(smaller_phi)-cos(larger_phi))/im;

      //cout<<"cap_area = "<<fixed<<setprecision(5)<<setw(8)<<cap_area/1E6<<" km^2."<<endl;
      //cout<<"nearby_area = "<<fixed<<setprecision(5)<<setw(8)<<nearby_area/1E6<<" km^2."<<endl;
      //cout<<"(cap_area-nearby_area)/cap_area = "<<fixed<<setprecision(5)<<setw(8)<<(cap_area-nearby_area)/cap_area<<endl;

    }//End of "while normalized area difference > high_tolerance"

    if(fabs(cap_area-nearby_area)/cap_area>=high_tolerance)
      converged=0;
      //cout<<"!!WARNING!! "<<areacounter<<" iterations were performed but areas are STILL out of tolerance!"<<endl;
    else converged=1;
  }//End of outer loop that jitters the lat/lon spacings.

  grid.options.cap_size = autogrid.options.cap_size;
  grid.options.lat_spacing = autogrid.options.lat_spacing;
  grid.options.lon_spacing = autogrid.options.lon_spacing;
  cout<<"Autosize iterated "<<l<<" times."<<endl;
}

void autosize_region_cap(grid_s &region){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  grid_s grid; //Temporary grid.

  grid.options = region.options;

  //Otherwise autosize complains.
  grid.options.area_type = 2;

  //Now use the regionified temp grid to autosize region cap.
  autosize_grid_cap(grid);

  //Copy autosized spacings to region.
  region.options.cap_size    = grid.options.cap_size;
  region.options.lat_spacing = grid.options.lat_spacing;
  region.options.lon_spacing = grid.options.lon_spacing;
}

int is_pt_in_bin(grid_s &grid, double current_lat, double current_lon,
                 long long i, int &newbin){
  /**********************************************************************
  Purpose: This function accepts the grid structure (via reference) and
          the index of the current bin, and a variable called "newbin"
          that, if set to 1, lets the function know that it needs to
          reset the bin boundaries for a new bin. The function then
          automatically sets newbin to 0 so subsequent calls of
          "is_pt_in_bin" use the previously defined boundaries.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          long long i - index of current bin (used in grid structure).
          int newbin - 1 if bin boundaries need to be recalced, else 0.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          Return value is 1 if pt is in bin, 0 if pt is not in bin.
          Also modifies grid structure with new boundaries if newbin=1.
  **********************************************************************/
  //Unless this variable is set to 1, assume that the pt in question
  //is NOT in this bin.
  int inbin=0;

  if(newbin == 1){
    //Define the size of the bin for each point, taking care to remain
    //inside the bounds of the -90..90 lat and 0..360 lon system.
    grid.maxlat = grid.lat.at(i) + grid.lat_halfwidth.at(i);
    grid.minlat = grid.lat.at(i) - grid.lat_halfwidth.at(i);
    grid.maxlon = grid.lon.at(i) + grid.lon_halfwidth.at(i);
    grid.minlon = grid.lon.at(i) - grid.lon_halfwidth.at(i);

    wrap_longitudes(grid);

    //If this is north pole, reset boundaries for the cap.
    if(grid.lat.at(i) == 90.0){
      //Set to 91 because below the current point is in the bin if
      //its lat is < maxlat or >= minlat. Needs to be like this
      //because otherwise points on the southern/northern borders get
      //chosen by less than (or more than) 1 bin.
      //So an easy fix is to set the NP cap's maxlat to 91,
      //otherwise it wouldn't include points at the north pole!
      grid.maxlat = 91.0;
    }
    //If this is south pole, reset boundaries for the cap.
    else if(grid.lat.at(i) == -90.0){
      grid.minlat = -90.0;
    }
    newbin = 0;//Now that bin boundaries are defined, set newbin to 0.
  }//End of "if newbin == 1"

  //Now that bin boundaries are defined (via equal grid or equal area
  //methods), check to see if the current point is in this bin.
  if(current_lat < grid.maxlat and current_lat >= grid.minlat){
    if((current_lon < grid.maxlonlarge and current_lon >= grid.minlonlarge) or (current_lon < grid.maxlonsmall and current_lon >= grid.minlonsmall)){
      inbin=1;
    }
  }

  //Debug... don't delete! You WILL need this at some point!
  /*if(fabs(current_lat-49.8750)<0.0001 and fabs(current_lon-108.0000)<0.0001 and fabs(grid.lat.at(i)-51.3944)<0.0001){
    cout<<"clat: "<<setw(18)<<fixed<<setprecision(14)<<current_lat<<", clon: "<<current_lon<<", inbin: "<<inbin<<endl;
    cout<<"minlat: "<<setw(18)<<fixed<<setprecision(14)<<grid.minlat<<", maxlat: "<<grid.maxlat<<",mnlons: "<<grid.minlonsmall<<",mxlons: "<<grid.maxlonsmall<<",mnlonl: "<<grid.minlonlarge<<",mxlonl: "<<grid.maxlonlarge<<endl;
  }//*/

  return inbin;
}

int is_pt_in_grid(grid_s &grid, double current_lat, double current_lon){
  /**********************************************************************
  Purpose: This function accepts the grid structure (via reference) and
          returns 1 if the current lat/lon coord is within boundaries.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          double current_lat/lon - Coordinates - are they in the grid?
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          Return value is 1 if pt is in grid, 0 if pt is not in grid.
  **********************************************************************/
  //Unless this variable is set to 1, assume that the pt in question
  //is NOT in the grid.
  int ingrid=0;

  grid.minlat = grid.options.boundaries[0];
  grid.maxlat = grid.options.boundaries[1];
  grid.minlon = grid.options.boundaries[2];
  grid.maxlon = grid.options.boundaries[3];

  wrap_longitudes(grid);

  //Set to 91 because below the current point is in the bin if
  //its lat is < maxlat or >= minlat. Needs to be like this
  //because otherwise points on the southern/northern borders get
  //chosen by less than (or more than) 1 bin.
  //So an easy fix is to set the NP cap's maxlat to 91,
  //otherwise it wouldn't include points at the north pole!
  if(grid.maxlat == 90.0) grid.maxlat = 91.0;

  //Now that grid boundaries are defined (via equal grid or equal area
  //methods), check to see if the current point is in this bin.
  if(current_lat < grid.maxlat and current_lat >= grid.minlat){
    if((current_lon < grid.maxlonlarge and current_lon >= grid.minlonlarge) or (current_lon < grid.maxlonsmall and current_lon >= grid.minlonsmall)){
      ingrid=1;
    }
  }
  return ingrid;
}

void wrap_longitudes(grid_s &grid){
  /**********************************************************************
  Purpose: This function accepts the grid structure (via reference) and
          wraps lon = 359 to lon = -1 and vice versa.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  if(grid.minlon < 0.0){
    grid.minlonsmall = grid.minlon;
    grid.minlonlarge = grid.minlon + 360.0;
  }
  else{
    grid.minlonsmall = grid.minlon;
    grid.minlonlarge = grid.minlon;
  }

  if(grid.maxlon >= 360.0){
    grid.maxlonsmall = grid.maxlon - 360.0;
    grid.maxlonlarge = grid.maxlon;
  }
  else{
    grid.maxlonsmall = grid.maxlon;
    grid.maxlonlarge = grid.maxlon;
  }
}

long long grid_index(grid_s &grid, double current_lat,
                     double current_lon, int verbose){
  /**********************************************************************
  Purpose: This function accepts the grid structure (via reference) and
          finds the index of the grid point that's closest to current_pt.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          double current_lat/lon - Point that's to be matched to grid.
          int verbose - 1/0 turns on/off messages.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          Return value is the index of the grid pt closest to current_pt.
  **********************************************************************/
  long long index = -1;//Initialize to impossible index.
  long long i=0,j=0;

  for(i=0;i<(long long)grid.lat.size();i++){
    //Set newbin to 1 so that bin boundaries
    //are recalculated in is_pt_in_bin
    int newbin = 1;
    if(is_pt_in_bin(grid, current_lat, current_lon, i, newbin) == 1){
      j++;//Note that matching bin was found at least once.
      index=i;
    }
  }//End of loop through grid points.

  if(j != 1){
    if(verbose) cout<<"Point at "<<current_lat<<", "<<current_lon<<" was chosen by "<<j<<" bins using fast code. ";
    index = grid_index_via_distance(grid, current_lat, current_lon, verbose);
  }

  return index;
}

long long grid_index_via_distance(grid_s &grid, double current_lat,
                                  double current_lon, int verbose){
  /**********************************************************************
  Purpose: This function accepts the grid structure (via reference) and
          finds the index of the grid point that's closest to current_pt.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          double current_lat/lon - Point that's to be matched to grid.
          int verbose - 1/0 turns on/off messages.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          Return value is the index of the grid pt closest to current_pt.
  **********************************************************************/
  long long index = -1;//Initialize to impossible index.
  long long i=0;

  double smallest_angle=twoPi,current_angle;//2pi is definitely too big!
  long long smallest_i = -1;
  //Calculate great circle angle to each grid point.
  for(i=0;i<(long long)grid.lat.size();i++){
    current_angle = spherical_distance(current_lat,current_lon,
                                       grid.lat.at(i),grid.lon.at(i));
    if(current_angle<smallest_angle){
      smallest_angle = current_angle;
      smallest_i = i;
    }
  }//End of loop through grid points.
  if(smallest_i >= 0) index = smallest_i;
  else cout<<"!!!WARNING!!! SHOULD NEVER SEE THIS!!! NJVDFUO32RI7"<<endl;
  if(verbose) cout<<"Distance to grid point at lat: "<<grid.lat.at(index)<<", lon: "<<grid.lon.at(index)<<" was smallest at "<<smallest_angle*planet_avg_radius<<" km."<<endl;
  return index;
}

void define_regional_grid(grid_s &grid, grid_s &region,
                          grid_s &regional_grid,
                          long long &i){
  /**********************************************************************
  Purpose: This function creates a "grid" structure based on the grid points
          in the current region, stores as regional_grid.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          long long i - Counter that holds the current region index.
                        Usually runs from 0 to region.lat.size()-1.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long j,temp;//counter,temp vars.

  regional_grid.options = grid.options;

  //Clear these vectors before repopulating them, just to be sure.
  //New grid variables should be added here.
  regional_grid.different_lats.clear();
  regional_grid.lat.clear();
  regional_grid.lon.clear();
  regional_grid.lat_halfwidth.clear();
  regional_grid.lon_halfwidth.clear();
  regional_grid.elev.clear();
  regional_grid.x.clear();
  regional_grid.y.clear();
  regional_grid.z.clear();
  regional_grid.area.clear();
  regional_grid.density.clear();
  regional_grid.cm2kg.clear();
  regional_grid.wcover.clear();
  regional_grid.wdepth.clear();
  //Clear support grid vectors.
  regional_grid.support_lat.clear(); regional_grid.support_lon.clear();
  regional_grid.support_x.clear(); regional_grid.support_y.clear();
  regional_grid.support_z.clear();
  regional_grid.support_weights.clear();
  regional_grid.main_weights.clear();

  //Resize grid vectors to the right size for this region.
  //New grid variables should be added here.
  regional_grid.lat.resize(region.all_grid_indices.at(i).size());
  regional_grid.lon.resize(region.all_grid_indices.at(i).size());
  regional_grid.lat_halfwidth.resize(region.all_grid_indices.at(i).size());
  regional_grid.lon_halfwidth.resize(region.all_grid_indices.at(i).size());
  regional_grid.elev.resize(region.all_grid_indices.at(i).size());
  regional_grid.x.resize(region.all_grid_indices.at(i).size());
  regional_grid.y.resize(region.all_grid_indices.at(i).size());
  regional_grid.z.resize(region.all_grid_indices.at(i).size());
  regional_grid.area.resize(region.all_grid_indices.at(i).size());
  regional_grid.density.resize(region.all_grid_indices.at(i).size());
  regional_grid.cm2kg.resize(region.all_grid_indices.at(i).size());
  regional_grid.wcover.resize(region.all_grid_indices.at(i).size());
  regional_grid.wdepth.resize(region.all_grid_indices.at(i).size());
  //Resize support grid vectors.
  regional_grid.support_lat.resize(region.all_grid_indices.at(i).size());
  regional_grid.support_lon.resize(region.all_grid_indices.at(i).size());
  regional_grid.support_x.resize(region.all_grid_indices.at(i).size());
  regional_grid.support_y.resize(region.all_grid_indices.at(i).size());
  regional_grid.support_z.resize(region.all_grid_indices.at(i).size());
  regional_grid.support_weights.resize(region.all_grid_indices.at(i).size());
  regional_grid.main_weights.resize(region.all_grid_indices.at(i).size());

  //Loop through all grid points in this region and add them to regional_grid.
  for(j=0;j<(long long)region.all_grid_indices.at(i).size();j++){
    //New grid variables should be added here.
    regional_grid.lat.at(j)=grid.lat.at(region.all_grid_indices.at(i).at(j));
    regional_grid.lon.at(j)=grid.lon.at(region.all_grid_indices.at(i).at(j));
    regional_grid.lat_halfwidth.at(j)=grid.lat_halfwidth.at(region.all_grid_indices.at(i).at(j));
    regional_grid.lon_halfwidth.at(j)=grid.lon_halfwidth.at(region.all_grid_indices.at(i).at(j));
    regional_grid.elev.at(j)=grid.elev.at(region.all_grid_indices.at(i).at(j));
    regional_grid.x.at(j)=grid.x.at(region.all_grid_indices.at(i).at(j));
    regional_grid.y.at(j)=grid.y.at(region.all_grid_indices.at(i).at(j));
    regional_grid.z.at(j)=grid.z.at(region.all_grid_indices.at(i).at(j));
    regional_grid.area.at(j)=grid.area.at(region.all_grid_indices.at(i).at(j));
    regional_grid.density.at(j)=grid.density.at(region.all_grid_indices.at(i).at(j));
    regional_grid.cm2kg.at(j)=grid.cm2kg.at(region.all_grid_indices.at(i).at(j));
    regional_grid.wcover.at(j)=grid.wcover.at(region.all_grid_indices.at(i).at(j));
    regional_grid.wdepth.at(j)=grid.wdepth.at(region.all_grid_indices.at(i).at(j));
    //Copy support grid vectors.
    regional_grid.support_lat.at(j)=grid.support_lat.at(region.all_grid_indices.at(i).at(j));
    regional_grid.support_lon.at(j)=grid.support_lon.at(region.all_grid_indices.at(i).at(j));
    regional_grid.support_x.at(j)=grid.support_x.at(region.all_grid_indices.at(i).at(j));
    regional_grid.support_y.at(j)=grid.support_y.at(region.all_grid_indices.at(i).at(j));
    regional_grid.support_z.at(j)=grid.support_z.at(region.all_grid_indices.at(i).at(j));
    regional_grid.support_weights.at(j)=grid.support_weights.at(region.all_grid_indices.at(i).at(j));
    regional_grid.main_weights.at(j)=grid.main_weights.at(region.all_grid_indices.at(i).at(j));

    //If the current lat value is "new" (not in different_lats)
    //then push_back the current lat.
    temp = count(regional_grid.different_lats.begin(),regional_grid.different_lats.end(),regional_grid.lat.at(j));
    if(temp == 0){
      regional_grid.different_lats.push_back(regional_grid.lat.at(j));
    }
  }

  //Debug.
  for(j=0;j<(long long)region.all_grid_indices.at(i).size();j++){
    //Check to see that lat/lon in regional_grid matches grid's values.
    if(grid.lat.at(region.all_grid_indices.at(i).at(j)) != regional_grid.lat.at(j)) cout<<"grid.lat["<<region.all_grid_indices.at(i).at(j)<<"] = "<<grid.lat.at(region.all_grid_indices.at(i).at(j))<<" but regional_grid.lat["<<j<<"] = "<< regional_grid.lat.at(j)<<endl;
    if(grid.lon.at(region.all_grid_indices.at(i).at(j)) != regional_grid.lon.at(j)) cout<<"grid.lon["<<region.all_grid_indices.at(i).at(j)<<"] = "<<grid.lon.at(region.all_grid_indices.at(i).at(j))<<" but regional_grid.lon["<<j<<"] = "<< regional_grid.lon.at(j)<<endl;
  }
}

void write_grid_to_cache(grid_s &grid){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long i,j;
  FILE *new_fp;
  string filename;
  string newfile = grid.options.cachefolder;

  filename=create_grid_cache_filename(grid.options);
  //cout<<"Writing grid to cache filename: "<<filename<<endl;
  newfile.append(filename);
  cout<<"Writing grid to cache file: "<<newfile<<endl;
  new_fp = fopen(newfile.c_str(),"w");
  fprintf(new_fp,"%20.12f\n",grid.options.lat_spacing);//After autosize
  fprintf(new_fp,"%20.12f\n",grid.options.lon_spacing);//After autosize
  fprintf(new_fp,"%20.12f\n",grid.options.cap_size);//After autosize
  //Write number of different lats as next line.
  fprintf(new_fp,"%lld\n",(long long)grid.different_lats.size());
  //Then write each "different lat" to the file.
  for(j=0;j<(long long)grid.different_lats.size();j++){
    fprintf(new_fp,"%20.12f\n",grid.different_lats.at(j));
  }
  //Now record positions/elevations of all grid points.
  //New grid variables should be added here.
  //FIX THIS EVENTUALLY BY MAKING EACH ENTRY A %s AND GIVING ITS WIDTH
  //EXPLICITLY EXACTLY THE SAME AS IN THE NEXT LINES!
  fprintf(new_fp,"            Latitude            Longitude       Lat Halfwidth       Lon Halfwidth  Elevation(m)                     x                     y                     z  distance_to_land(km) distance_to_ocean(km)                  Area               Density                 cm2kg        Water coverage   Mean depth of water\n");
  for(i=0;i<(long long)grid.lat.size();i++){
    fprintf(new_fp,"%20.12f%21.12f%20.12f%20.12f%14.2f%22.14f%22.14f%22.14f%22.5f%22.5f%22.5f%22.5f%22.5f%22.5f%22.5f\n",grid.lat.at(i),grid.lon.at(i),grid.lat_halfwidth.at(i),grid.lon_halfwidth.at(i),grid.elev.at(i),grid.x.at(i),grid.y.at(i),grid.z.at(i),grid.distance_to_land.at(i),grid.distance_to_ocean.at(i),grid.area.at(i),grid.density.at(i),grid.cm2kg.at(i),grid.wcover.at(i), grid.wdepth.at(i));
  }
  fclose(new_fp);
}

void load_grid_from_cache(grid_s &grid){
  /***********************************************************************
  Purpose: This function
  ************************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ************************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************/
  long long j;
  double min_lat,max_lat;//Edge values for bin centers-
                         //uses boundaries and spacings.
  double current_lat_halfwidth,current_lon_halfwidth;//Temp vars.
  double current_area,current_density,current_cm2kg;//Temp vars.
  long long smallest_k;//stores index of nearest topo point.
  //These variables are used to load a matching grid from cache.
  FILE *in_fp;
  double onelat,onelon,oneelev,onewc,onemd;
  //These variables are used to store a new cache file, or load an old one.
  string newfile = grid.options.cachefolder;
  string filename;//stores JUST the filename of cache file (not the path).
  char s[max_length];
  //These variables were added in the process of xyz coord conversion.
  string temp_string;
  double x,y,z;//temp vars for grid point -> xyz coord calculation.

  char* f_return;//Used to stop fgets return value warning.

  filename=create_grid_cache_filename(grid.options);
  cout<<"Loading grid from cache file "<<filename<<"..."<<endl;
  //Prepend directory to filename, then open.
  newfile.append(filename);
  in_fp = fopen(newfile.c_str(),"r");
  //Load the real lat spacing- after the autoselect code if that engaged
  f_return=fgets(s,max_length,in_fp);
  sscanf(s,"%lf", &onelat);
  grid.options.lat_spacing = onelat;
  //Load the real lon spacing- after the autoselect code if that engaged
  f_return=fgets(s,max_length,in_fp);
  sscanf(s,"%lf", &onelon);
  grid.options.lon_spacing = onelon;
  //Load the real cap size- after the autoselect code if that engaged
  f_return=fgets(s,max_length,in_fp);
  sscanf(s,"%lf", &onelon);
  grid.options.cap_size = onelon;
  //Load the next line which contains number of different lats.
  f_return=fgets(s,max_length,in_fp);
  sscanf(s,"%lld", &smallest_k);
  //Use # of diff lats to resize vector.
  grid.different_lats.resize(smallest_k);
  //Read in different lats.
  for(j=0;j<(int)grid.different_lats.size();j++){
    f_return=fgets(s,max_length,in_fp);
    sscanf(s,"%lf", &onelat);
    grid.different_lats.at(j) = onelat;
  }

  //Load the next header line separately because it contains no data.
  f_return=fgets(s,max_length,in_fp);
  //Load each line of data as a string, parse into numeric values,
  //then add each number to its respective vector.
  //New grid variables should be added here.
  while(fgets(s,max_length,in_fp)!=NULL){
    sscanf(s,"%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", &onelat, &onelon, &current_lat_halfwidth, &current_lon_halfwidth, &oneelev, &x, &y, &z, &min_lat, &max_lat, &current_area, &current_density, &current_cm2kg, &onewc,&onemd);
    //cout<<s<<endl<<"lat: "<<onelat<<", lon: "<<onelon<<", wc: "<<onewc<<". md: "<<onemd<<endl<<endl;
    grid.lat.push_back(onelat);
    grid.lon.push_back(onelon);
    grid.elev.push_back(oneelev);
    grid.lat_halfwidth.push_back(current_lat_halfwidth);
    grid.lon_halfwidth.push_back(current_lon_halfwidth);
    grid.x.push_back(x);
    grid.y.push_back(y);
    grid.z.push_back(z);
    grid.distance_to_land.push_back(min_lat);
    grid.distance_to_ocean.push_back(max_lat);
    grid.area.push_back(current_area);
    grid.density.push_back(current_density);
    grid.cm2kg.push_back(current_cm2kg);
    grid.wcover.push_back(onewc);
    grid.wdepth.push_back(onemd);
  }
  fclose(in_fp);
  cout<<"Finished. "<<(long long)grid.lat.size()<<" grid points loaded."<<endl;
}

void define_support_grid(grid_s &grid){
  /**********************************************************************
  Purpose: This function generates a support grid based on input params.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k,check1=-1,check2=-1,check3=-1;
  double current_lat=0,current_lon=0;
  //These variables are used to search the cache folder for matching grids.
  DIR *cacheDIR;
  struct dirent *cacheDirEnt;
  //These variables are used to load a matching grid from cache.
  FILE *in_fp;
  double onelat,onelon;
  //These variables are used to store a new cache file.
  string newfile = grid.options.cachefolder;
  string filename;//stores JUST the filename of cache file (not the path).
  char s[max_length];
  FILE *new_fp;
  //These variables were added in the process of xyz coord conversion.
  string temp_string;
  double x,y,z;//temp vars for grid point -> xyz coord calculation.

  //Used to layout the support grid points.
  double lat_stepsize,lon_stepsize;

  long long support_counter,messages_counter,max_messages;

  double normalization=0.0;//Used to normalize weighting factors.

  max_messages = 4;//Room enough for both poles and 2 other points.

  //Variables dealing with weighting.
  //For poles, only include supp pts.  For other pts, also include main pt.
  vector<double> diff_support_lats;
  vector<long long> number_at_support_lat;
  vector<double>::iterator idsl;//Used to search diff_support_lats.
  long long dsli;//idsl converted to index for accessing number_at_supp_lat.

  double degrees_to_closest_support_lat;//For pole, nearest supp pt,spacing.

  long long tempcounter;

  vector<int> number_of_times_weight_recorded;
  double larger_phi,smaller_phi,current_area;

  char* f_return;//Used to stop fgets return value warning.

  //Search for previously computed grids in the cache, but only if
  //noload == 0 and this isn't a custom grid type.
  if(grid.options.noload == 0 and grid.options.type !=0){

    //Check to see if the cache contains a file with the same options
    //as the ones loaded in grid.options.  If so, set i=1.
    i=0;//Init as 0 because it hasn't found matching grid in cache yet.

    //First, figure out exactly what filename we're looking for by
    //creating a filename that corresponds to currently selected options.
    filename = create_support_grid_cache_filename(grid.options);

    //Open the cache folder.
    cacheDIR = opendir(grid.options.cachefolder.c_str());

    //Examine each file, see if it contains the desired grid.
    cacheDirEnt = readdir(cacheDIR);
    while(cacheDirEnt != NULL && i == 0){
      //If this filename matches desired grid options, set i=1 to exit loop.
      if(strcmp(filename.c_str(),cacheDirEnt->d_name)==0) i = 1;

      //printf("%s\n",cacheDirEnt->d_name);
      cacheDirEnt = readdir(cacheDIR);
    }

    // Release the open folder.
    closedir(cacheDIR);

    //If matching file was found, load it and skip rest of function.
    if(i == 1){
      cout<<"Loading support grid from cache..."<<endl;

      //Initialize all vectors.
      grid.support_lat.clear(); grid.support_lon.clear();
      grid.support_x.clear(); grid.support_y.clear();
      grid.support_z.clear();

      grid.support_lat.resize(grid.lat.size());
      grid.support_lon.resize(grid.lat.size());
      grid.support_x.resize(grid.lat.size());
      grid.support_y.resize(grid.lat.size());
      grid.support_z.resize(grid.lat.size());
      grid.support_weights.resize(grid.lat.size());
      grid.main_weights.resize(grid.lat.size());

      //Prepend directory to filename, then open.
      newfile.append(filename);
      in_fp = fopen(newfile.c_str(),"r");
      //Load the header line separately because it contains no data.
      f_return=fgets(s,max_length,in_fp);

      for(i=0;i<(long long)grid.lat.size();i++){
        //First read the number of support grid points for this main gr pt.
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lld", &j);
        long long support_numpoints = j;
        //Then read main_weight for this main gr pt.
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf", &onelat);
        grid.main_weights.at(i)=onelat;
        //Now read each supporting grid point's coords and weights.
        for(j=0;j<support_numpoints;j++){
          f_return=fgets(s,max_length,in_fp);
          sscanf(s,"%lf %lf %lf %lf %lf %lf", &onelat, &onelon, &x, &y, &z, &normalization);
          grid.support_lat.at(i).push_back(onelat);
          grid.support_lon.at(i).push_back(onelon);
          grid.support_x.at(i).push_back(x);
          grid.support_y.at(i).push_back(y);
          grid.support_z.at(i).push_back(z);
          grid.support_weights.at(i).push_back(normalization);
        }
      }
      fclose(in_fp);
      cout<<"Finished."<<endl;

      //Skip the loading code using a (gasp!) goto function.
      goto end_of_support_grid_loading;
    }
  }

  //If support grid needs to be computed, layout points in manner described
  //by "support_type".
  cout<<"Defining supporting grid..."<<endl;
  switch(grid.options.support_type){
    default: cout<<"!!!!WARNING!!!!!! Supporting grid type "<<grid.options.type<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 0:{
      //Initialize all vectors.
      grid.support_lat.clear(); grid.support_lon.clear();
      grid.support_x.clear(); grid.support_y.clear();
      grid.support_z.clear();

      grid.support_lat.resize(grid.lat.size());
      grid.support_lon.resize(grid.lat.size());
      grid.support_x.resize(grid.lat.size());
      grid.support_y.resize(grid.lat.size());
      grid.support_z.resize(grid.lat.size());
      grid.support_weights.resize(grid.lat.size());
      grid.main_weights.resize(grid.lat.size(),1.0);//Needs to be 1.

      cout <<"Skipping support grid creation."<<endl;
      return;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{

      //Initialize all vectors.
      grid.support_lat.clear(); grid.support_lon.clear();
      grid.support_x.clear(); grid.support_y.clear();
      grid.support_z.clear();

      grid.support_lat.resize(grid.lat.size());
      grid.support_lon.resize(grid.lat.size());
      grid.support_x.resize(grid.lat.size());
      grid.support_y.resize(grid.lat.size());
      grid.support_z.resize(grid.lat.size());
      grid.support_weights.resize(grid.lat.size());
      //If mult=0, needs to be 1.
      grid.main_weights.resize(grid.lat.size(),1.0);

      //Supporting grid can be skipped by setting either multiplier to < 1.
      if(grid.options.support_lat_multiplier < 1 or grid.options.support_lon_multiplier < 1){
        cout <<"Skipping support grid creation."<<endl;
        return;
      }
      //If either multiplier is equal to 1, use code designed to only
      //attach 4 support points to each main grid point, rather than the
      //8 grid points with the normal code with multiplier=2.
      else if(grid.options.support_lat_multiplier == 1 or grid.options.support_lon_multiplier == 1){
        cout<<"!!!WARNING!!! SUPPORT POINT MULT==1 CODE WAS DELETED BECAUSE IT WAS USELESS AND SIMPLY ADDED CODE BLOAT!!!!"<<endl;
      }//End of "is either multiplier equal to 1?"
      else{//Normal code, where multipliers are both >1.
        messages_counter=0;//No messages printed yet.
        //Unless I define a special lon_multiplier for the poles, they
        //don't have enough support grid points relative to other main pts.
        //when mult=4, +2 gives pole 36 and others have 48.
        //when mult=4, +3 gives pole 42 and others have 48.
        //when mult=4, +4 gives pole 48 and others have 48. SAME AS *2!
        //when mult=3, +4 gives pole 28 and others have 24.
        //when mult=3, *2 gives pole 24 and others have 24.
        //Times 2 it is (seems to work all the way up to mult=6).
        long long pole_lon_multiplier=grid.options.support_lon_multiplier*2;

        for(i=0;i<(long long)grid.lat.size();i++){

          if((messages_counter<max_messages and grid.lat.at(i) != 90.0 and grid.lat.at(i) != -90.0) or i % 100 == 0){
            cout<<endl<<"Creating support points for main grid point "<<i+1<<" (of "<<grid.lat.size()<<") with lat "<<grid.lat.at(i)<<" and lon "<<grid.lon.at(i)<<endl;
            messages_counter++;
          }
          support_counter=0;//New main grid pt so reset num of support pts.

          //Treat poles differently than other grid points.
          if(grid.lat.at(i) == 90.0 or grid.lat.at(i) == -90.0){
            lat_stepsize = grid.lat_halfwidth.at(i)/(grid.options.support_lat_multiplier-0.5);//.5 so supp pts evenly spaced.
            lon_stepsize = grid.lon_halfwidth.at(i)/pole_lon_multiplier;

            //Loop across latitudes (skip edges of cap).
            for(k=1;k<grid.options.support_lat_multiplier;k++){
              //Loop across longitudes.
              for(j=-pole_lon_multiplier;j<pole_lon_multiplier;j++){

                support_counter++;
                current_lat = grid.lat.at(i)-k*lat_stepsize;
                current_lon = grid.lon.at(i)+j*lon_stepsize;
                cout<<"Support point #"<<support_counter<<" has lat "<<current_lat<<" and lon "<<current_lon<<endl;

                grid.support_lat.at(i).push_back(current_lat);
                grid.support_lon.at(i).push_back(current_lon);

                //Convert grid lat/lon/radius to x,y,z.
                llh2xyz(x,y,z,current_lat,current_lon,0.0);
                grid.support_x.at(i).push_back(x);
                grid.support_y.at(i).push_back(y);
                grid.support_z.at(i).push_back(z);
              }//Loop through longitudes.
            }//Loop through latitudes.
          }//End of polar statements.
          else{
            lat_stepsize = grid.lat_halfwidth.at(i)/(grid.options.support_lat_multiplier-0.5);//.5 so supp pts evenly spaced.
            lon_stepsize = grid.lon_halfwidth.at(i)/(grid.options.support_lon_multiplier-0.5);//.5 so supp pts evenly spaced.
            //Loop across latitudes (skip edges of bin AND main grid point).
            for(k=-grid.options.support_lat_multiplier+1; k<grid.options.support_lat_multiplier;k++){
              //Loop across longitudes (skip edges of bin AND main gr pt).
              for(j=-grid.options.support_lon_multiplier+1; j<grid.options.support_lon_multiplier;j++){
                //If this isn't the location of the main grid point.
                if(k!=0 or j!=0){
                  support_counter++;
                  current_lat = grid.lat.at(i)+k*lat_stepsize;
                  current_lon = grid.lon.at(i)+j*lon_stepsize;
                  if(messages_counter<max_messages)
                    cout<<"Support point #"<<support_counter<<" has lat "<<current_lat<<" and lon "<<current_lon<<endl;

                  grid.support_lat.at(i).push_back(current_lat);
                  grid.support_lon.at(i).push_back(current_lon);

                  //Convert grid lat/lon/radius to x,y,z.
                  llh2xyz(x,y,z,current_lat,current_lon,0.0);
                  grid.support_x.at(i).push_back(x);
                  grid.support_y.at(i).push_back(y);
                  grid.support_z.at(i).push_back(z);
                }//If this isn't the main grid point.
              }//Loop through longitudes.
            }//Loop through latitudes.
          }//If this is a non-polar main grid point.
        }//End of loop through main grid points.
      }//End of "if both multipliers are greater than 1".
      break;
    }
  }//End of switch-case.

  //Calculate weighting factors here, because as far as I can tell this
  //should be universal- but it can always be copied into case 1 if I decide
  //to do this differently when and if I write cases >1.
  cout<<"Calculating weighting factors for each support pt to account for area differences..."<<endl;
  for(i=0;i<(long long)grid.lat.size();i++){
    normalization=0.0;//Reset for each main grid point.

    //Resize support_weights so it has an entry for every supp pt of main pt
    grid.support_weights.at(i).resize(grid.support_lat.at(i).size());

    //Reset vectors.
    diff_support_lats.clear();
    number_at_support_lat.clear();
    number_of_times_weight_recorded.clear();
    number_of_times_weight_recorded.resize(grid.support_lat.at(i).size(),0);

    //Need to treat poles differently because main gr pt at pole has
    //totally different area calculation than its support pts.
    if(grid.lat.at(i) == 90.0){

      //Reset degrees to closest support pt lat to large value.
      degrees_to_closest_support_lat=200;

      //Loop thru supp pts to see where they are, and how many at diff lats.
      for(j=0;j<(long long)grid.support_lat.at(i).size();j++){

        //Is this closest lat to pole?  If so, record degrees to the pole.
        //I'm assuming this is also the N-S spacing between supp gr pts.
        if(grid.lat.at(i)-grid.support_lat.at(i).at(j)<degrees_to_closest_support_lat)
          degrees_to_closest_support_lat = grid.lat.at(i) - grid.support_lat.at(i).at(j);

        //Has this support_lat been seen yet?
        dsli = -1;
        idsl = find(diff_support_lats.begin(),diff_support_lats.end(),grid.support_lat.at(i).at(j));
        //If this lat has been seen, increment appropriate entry in n_a_s_l.
        if(idsl != diff_support_lats.end()){
          dsli = (long long)(idsl - diff_support_lats.begin());
          number_at_support_lat.at(dsli)++;
        }
        else{//New lat, so add to diff_support_lats vector.
          diff_support_lats.push_back(grid.support_lat.at(i).at(j));
          number_at_support_lat.push_back(1);//There's already 1 at this lat
        }
      }//End of loop through support gr pts for this main gr pt.

      //Make sure number_at_support_lat sums to grid.support_lat.at(i).size()
      tempcounter=0;
      for(j=0;j<(long long)number_at_support_lat.size();j++)
        tempcounter += number_at_support_lat.at(j);

      if(tempcounter != (long long)grid.support_lat.at(i).size())
        cout<<"!!!WARNING!!! SHOULD NEVER EVER SEE THIS!!! JVDfddf8"<<endl;

      //Calculate area of main gr pt, store as its weight.
      //Note that area associated with main gr pt extends halfway to the
      //nearest support_lat.
      grid.main_weights.at(i)=(pow(planet_avg_radius,2)*1E6)*twoPi*(cos(0.0)-cos(degrees_to_closest_support_lat/2.0*deg2rad));
      normalization += grid.main_weights.at(i);

      //Calculate area of supp pts by looping through diff_support_lats
      //then looping through grid.support_lats.at(i) to see which supp pts
      //have lats that match the current diff_support_lats.
      //Keep a record of how many times each supp pt has had its area
      //recorded to verify each supp pt has its area recorded exactly once.
      for(k=0;k<(long long)diff_support_lats.size();k++){

        //Calculate area for supp pts at this lat
        larger_phi=(90.-(diff_support_lats.at(k)-degrees_to_closest_support_lat/2.0))*deg2rad;
        smaller_phi=(90.-(diff_support_lats.at(k)+degrees_to_closest_support_lat/2.0))*deg2rad;

        //Just checkin'
        if(larger_phi<0 or smaller_phi<0 or smaller_phi>=larger_phi)
          cout<<"!!!!!WARNING!!! larger_phi = "<<larger_phi<<" and smaller_phi = "<<smaller_phi<<" at grid.lat["<<i<<"] = "<<grid.lat.at(i)<<endl;

        //Each grid point at this lat has this area: (1E6-km^2 to m^2)
        current_area = (pow(planet_avg_radius,2)*1E6)*twoPi*(cos(smaller_phi)-cos(larger_phi))/number_at_support_lat.at(k);

        //Now find supp pts with this lat and record the area as the weight.
        for(j=0;j<(long long)grid.support_lat.at(i).size();j++){
          if(grid.support_lat.at(i).at(j) == diff_support_lats.at(k)){
            grid.support_weights.at(i).at(j) = current_area;
            number_of_times_weight_recorded.at(j)++;
            normalization += current_area;
          }
        }//End of loop through support grid points.
      }//End of loop through different support lats.

      //Check to see that each supp pt only had its area recorded once.
      for(j=0;j<(long long)grid.support_lat.at(i).size();j++)
        if(number_of_times_weight_recorded.at(j) != 1)
          cout<<"!!!!WARNING!!!! SHOULD NEVER SEE THIS!!! AREA FOR SUPP PT["<<i<<"]["<<j<<"] WAS RECORDED A TOTAL OF "<<number_of_times_weight_recorded.at(j)<<" TIMES!!!"<<endl;

    }//End of "if main gr pt is north pole".
    else if(grid.lat.at(i) == -90.0){
      cout<<"HAVEN'T COPIED FROM NORTH POLE THEN ALTERED FOR SOUTH POLE YET!"<<endl;
    }//End of "if main gr pt is south pole".
    else{//Otherwise this is just a normal point, which is much easier.

      //Normal points include the main grid point in these vectors.
      diff_support_lats.push_back(grid.lat.at(i));
      number_at_support_lat.push_back(1);//There's already 1 at this lat

      //Loop thru supp pts to see where they are, and how many at diff lats.
      for(j=0;j<(long long)grid.support_lat.at(i).size();j++){

        //Has this support_lat been seen yet?
        dsli = -1;
        idsl = find(diff_support_lats.begin(),diff_support_lats.end(),grid.support_lat.at(i).at(j));
        //If this lat has been seen, increment appropriate entry in n_a_s_l.
        if(idsl != diff_support_lats.end()){
          dsli = (long long)(idsl - diff_support_lats.begin());
          number_at_support_lat.at(dsli)++;
        }
        else{//New lat, so add to diff_support_lats vector.
          diff_support_lats.push_back(grid.support_lat.at(i).at(j));
          number_at_support_lat.push_back(1);//There's already 1 at this lat
        }
      }//End of loop through support gr pts for this main gr pt.

      //Make sure number_at_support_lat sums to grid.support_lat.at(i).size()
      //+1 (because, again, normal pts include the main grid point!)
      tempcounter=0;
      for(j=0;j<(long long)number_at_support_lat.size();j++)
        tempcounter += number_at_support_lat.at(j);

      if(tempcounter != (long long)grid.support_lat.at(i).size()+1)//+1-include main pt!
        cout<<"!!!WARNING!!! SHOULD NEVER EVER SEE THIS!!! vnei3frb8"<<endl;

      //Weight for main gr pt starts at equal weighting value.  Other
      //pts are weighted according to sin(colat) and number of total pts-
      //support AND main- at that colat to account for area differences.
      double orig_main_weight=1.0/(double)(grid.support_lat.at(i).size()+1);
      grid.main_weights.at(i)=orig_main_weight;//Because real weight changes

      //Calculate weight of supp pts by looping through diff_support_lats
      //then looping through grid.support_lats.at(i) to see which supp pts
      //have lats that match the current diff_support_lats.
      //Keep a record of how many times each supp pt has had its weight
      //recorded to verify each supp pt has its wt recorded exactly once.
      int mainweightadjusted=0;//just checking...
      for(k=0;k<(long long)diff_support_lats.size();k++){
        //If this is the diff_lat where the main gr pt is, divide its weight
        //by the number of pts at this lat so that it is treated the same
        //as the support grid pts.
        if(grid.lat.at(i) == diff_support_lats.at(k)){
          grid.main_weights.at(i) /= (double)number_at_support_lat.at(k);
          normalization += grid.main_weights.at(i);//Needed to wait to norm.
          mainweightadjusted++;
        }

        //Now find supp pts with this lat and record their weight.
        for(j=0;j<(long long)grid.support_lat.at(i).size();j++){
          if(grid.support_lat.at(i).at(j) == diff_support_lats.at(k)){
            grid.support_weights.at(i).at(j) = orig_main_weight*sin((90-grid.support_lat.at(i).at(j))*deg2rad)/sin((90-grid.lat.at(i))*deg2rad)/((double)number_at_support_lat.at(k));
            normalization += grid.support_weights.at(i).at(j);
            number_of_times_weight_recorded.at(j)++;
          }
        }//End of loop through support grid points.
      }//End of loop through different support lats.

      //Just checking.
      if(mainweightadjusted != 1)
        cout<<"!!!WARNING!!! Main weight for pt with i="<<i<<" has been adjusted "<<mainweightadjusted<<" times."<<endl;

      //Check to see that each supp pt only had its weight recorded once.
      for(j=0;j<(long long)grid.support_lat.at(i).size();j++)
        if(number_of_times_weight_recorded.at(j) != 1)
          cout<<"!!!!WARNING!!!! SHOULD NEVER SEE THIS!!! WEIGHT FOR SUPP PT["<<i<<"]["<<j<<"] WAS RECORDED A TOTAL OF "<<number_of_times_weight_recorded.at(j)<<" TIMES!!!"<<endl;

    }//End of "if main gr pt not a polar point"

    //Now normalize all weights so they sum to 1.0 (supp AND main weights)
    grid.main_weights.at(i) /= normalization;
    for(j=0;j<(long long)grid.support_lat.at(i).size();j++){
      grid.support_weights.at(i).at(j) /= normalization;
    }//End of normalization loop thru support gr pts for this main gr pt.

    //Detailed checks of the support grid weighting.
    if(1==2){
      cout<<"grid.lat["<<i<<"] = "<<fixed<<setprecision(1)<<setw(8)<<grid.lat.at(i)<<" grid.lon["<<i<<"] = "<<grid.lon.at(i)<<" has weight "<<fixed<<setprecision(10)<<setw(15)<<grid.main_weights.at(i)<<endl;

      for(j=0;j<(long long)diff_support_lats.size();j++){
        cout<<"Diff lats #"<<j+1<<" at lat "<<fixed<<setprecision(1)<<setw(8)<<diff_support_lats.at(j)<<" has "<<number_at_support_lat.at(j)<<" support points and main pts."<<endl;
      }//End of loop through different support lats.

      for(j=0;j<(long long)grid.support_lat.at(i).size();j++){
        cout<<"Support pt #"<<j+1<<" at lat "<<fixed<<setprecision(1)<<setw(8)<<grid.support_lat.at(i).at(j)<<" and lon "<<grid.support_lon.at(i).at(j)<<" has weight "<<fixed<<setprecision(10)<<setw(15)<<grid.support_weights.at(i).at(j)<<endl;
      }//End of normalization loop thru support gr pts for this main gr pt.
      current_area=grid.main_weights.at(i);//using current_area as temp var.
      for(j=0;j<(long long)grid.support_lat.at(i).size();j++)
        current_area += grid.support_weights.at(i).at(j);

      cout<<"Main and support weights sum to: "<<fixed<<setprecision(10)<<setw(15)<<current_area<<endl;
      cout<<endl;
    }

  }//End of loop through main grid.

  //After creating supporting grid, save it in the cache so it can be
  //retrieved more quickly the next time this same grid is requested.
  //But only if the main grid isn't custom.
  if(grid.options.type != 0){
    filename=create_support_grid_cache_filename(grid.options);
    newfile.append(filename);
    cout<<"Writing supporting grid to cache file: "<<newfile<<endl;
    new_fp = fopen(newfile.c_str(),"w");
    fprintf(new_fp,"            Latitude            Longitude                     x                     y                     z                Weight\n");
    for(i=0;i<(long long)grid.lat.size();i++){
      //Write number of support grid points for this main grid point.
      fprintf(new_fp,"%lld\n",(long long)grid.support_lat.at(i).size());
      //Write weight for main grid point.
      fprintf(new_fp,"%22.14f\n",grid.main_weights.at(i));
      //Write coords and weights for all support grid points.
      for(j=0;j<(long long)grid.support_lat.at(i).size();j++){
        fprintf(new_fp,"%21.12f%20.12f%22.14f%22.14f%22.14f%22.14f\n",grid.support_lat.at(i).at(j),grid.support_lon.at(i).at(j),grid.support_x.at(i).at(j),grid.support_y.at(i).at(j),grid.support_z.at(i).at(j),grid.support_weights.at(i).at(j));
      }
    }
    fclose(new_fp);
  }

  end_of_support_grid_loading://Successful loading code jumps to this point.

  //Used to report the distances between main grid points and closest
  //support grid points.
  double temp_double,temp_double2,temp_double3, closest_north_pole_support_spacing, farthest_north_pole_support_spacing, closest_south_pole_support_spacing, farthest_south_pole_support_spacing, max_closest_support_spacing, min_closest_support_spacing, avg_closest_support_spacing, max_farthest_support_spacing, min_farthest_support_spacing, avg_farthest_support_spacing;
  long long mainindex_for_min_closest_support_spacing, mainindex_for_max_closest_support_spacing, mainindex_for_min_farthest_support_spacing, mainindex_for_max_farthest_support_spacing, num_support_points;

  //Perform consistency checks and see how many supporting grid points
  //are defined for each main grid point.  Also calculate distances from
  //main grid points to their closest support grid points.
  //Initializations for each type of maximimation or averaging or indexing:
  closest_north_pole_support_spacing = 1000.0;
  farthest_north_pole_support_spacing = 0.0;
  closest_south_pole_support_spacing = 1000.0;
  farthest_south_pole_support_spacing = 0.0;
  max_closest_support_spacing = 0.0;
  min_closest_support_spacing = 1000.0;
  avg_closest_support_spacing = 0.0;
  max_farthest_support_spacing = 0.0;
  min_farthest_support_spacing = 1000.0;
  avg_farthest_support_spacing = 0.0;
  mainindex_for_min_closest_support_spacing = -1;
  mainindex_for_max_closest_support_spacing = -1;
  mainindex_for_min_farthest_support_spacing = -1;
  mainindex_for_max_farthest_support_spacing = -1;
  num_support_points=0;
  for(i=0;i<(long long)grid.lat.size();i++){

    //Check to see how many supp gr pts are associated w each main gr pt.
    if(grid.lat.at(i) == 90.0) check1 = grid.support_lat.at(i).size();
    else if(grid.lat.at(i) == -90.0) check2 = grid.support_lat.at(i).size();
    else if(check3==-1) check3 = grid.support_lat.at(i).size();
    else if((long long)grid.support_lat.at(i).size() != check3) cout<<"WARNING!  ALL MAIN GRID POINTS THAT AREN'T POLES SHOULD HAVE THE SAME NUMBER OF SUPPORT GRID POINTS, BUT MAIN GRID POINT #"<<i<<" AT LAT "<<grid.lat.at(i)<<" AND LON "<<grid.lon.at(i)<<" HAS "<<grid.support_lat.at(i).size()<<" SUPPORT POINTS, BUT AT LEAST ONE PREVIOUS MAIN (NON-POLAR) GRID POINT HAD "<<check3<<" SUPPORT GRID POINTS!!!"<<endl;

    //Now check for average spacing between support points.

    //Every main grid pt should reset the smallest distance to closest (2)
    //and farthest (3) supp gr pts so the average dist from each main gr
    //point to closest/farthest supp grid point can be summed.
    temp_double2=1000.0; temp_double3=0.0;

    //If this is N pole, calc distance from pole to nearest supp gr pt
    if(grid.lat.at(i) == 90.0){
      for(j=0;j<(long long)grid.support_lat.at(i).size();j++){

        temp_double = planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i), grid.support_lat.at(i).at(j),grid.support_lon.at(i).at(j));
        if(temp_double<closest_north_pole_support_spacing)
          closest_north_pole_support_spacing=temp_double;
        if(temp_double>farthest_north_pole_support_spacing)
          farthest_north_pole_support_spacing=temp_double;
      }//End of loop through support grid points.
      avg_closest_support_spacing += temp_double2;
      avg_farthest_support_spacing += temp_double3;
      num_support_points++;
    }//End of "if this is a N pole point.
    //If this is S pole, calc distance from pole to nearest supp gr pt
    else if(grid.lat.at(i) == -90.0){
      for(j=0;j<(long long)grid.support_lat.at(i).size();j++){

        temp_double = planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i), grid.support_lat.at(i).at(j),grid.support_lon.at(i).at(j));
        if(temp_double<closest_south_pole_support_spacing)
          closest_south_pole_support_spacing=temp_double;
        if(temp_double>farthest_south_pole_support_spacing)
          farthest_south_pole_support_spacing=temp_double;
      }//End of loop through support grid points.
      avg_closest_support_spacing += temp_double2;
      avg_farthest_support_spacing += temp_double3;
      num_support_points++;
    }//End of "if this is a S pole point.
    //Otherwise for all other main grid points calculate max/min distances
    //as well as summing for the average distance.
    else{
      for(j=0;j<(long long)grid.support_lat.at(i).size();j++){

        temp_double = planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i), grid.support_lat.at(i).at(j),grid.support_lon.at(i).at(j));
        //If this is the closest supporting grid point to this main grid pt,
        //reset the smallest current distance stored in temp_double2.
        if(temp_double<temp_double2)
          temp_double2=temp_double;
        //If this is the farthest supporting grid point to this main gr pt,
        //reset the farthest current distance stored in temp_double3.
        if(temp_double>temp_double3)
          temp_double3=temp_double;
        //If this is the closest a non-polar main grid point has EVER been
        //seen to a supporting grid point, note that.
        if(temp_double<min_closest_support_spacing){
          min_closest_support_spacing=temp_double;
          mainindex_for_min_closest_support_spacing=i;
        }
        //If this is the farthest a non-polar main grid point has EVER been
        //seen from a supporting grid point, note that.
        if(temp_double>max_farthest_support_spacing){
          max_farthest_support_spacing=temp_double;
          mainindex_for_max_farthest_support_spacing=i;
        }
      }//End of loop through support grid points.

      //If this is the farthest distance to the closest supp gr pt seen for
      //ANY non-polar main grid point, note that.
      if(temp_double2>max_closest_support_spacing){
        max_closest_support_spacing=temp_double2;
        mainindex_for_max_closest_support_spacing=i;
      }

      //If this is the smallest distance to the farthest supp gr pt seen for
      //ANY non-polar main grid point, note that.
      if(temp_double3<min_farthest_support_spacing){
        min_farthest_support_spacing=temp_double3;
        mainindex_for_min_farthest_support_spacing=i;
      }
      avg_closest_support_spacing += temp_double2;
      avg_farthest_support_spacing += temp_double3;
      num_support_points++;
    }//Else if this is not a polar point.
  }//End of loop through main grid.

  //Divide average closest/farthest support spacings by number of
  //"closest/farthest support points" summed up to create them.
  avg_closest_support_spacing /= (double)num_support_points;
  avg_farthest_support_spacing /= (double)num_support_points;

  if(check1 != -1){
    cout<<"North pole has "<<check1<<" support grid points."<<endl;
    cout<<"Distance from north pole to closest supporting grid point: "<<fixed<<setprecision(1)<<setw(5)<<closest_north_pole_support_spacing<<" km."<<endl;
    cout<<"Distance from north pole to farthest supporting grid point: "<<fixed<<setprecision(1)<<setw(5)<<farthest_north_pole_support_spacing<<" km."<<endl;
  }
  if(check2 != -1){
    cout<<"South pole has "<<check2<<" support grid points."<<endl;
    cout<<"Distance from south pole to closest supporting grid point: "<<fixed<<setprecision(1)<<setw(5)<<closest_south_pole_support_spacing<<" km."<<endl;
    cout<<"Distance from south pole to farthest supporting grid point: "<<fixed<<setprecision(1)<<setw(5)<<farthest_south_pole_support_spacing<<" km."<<endl;
  }
  if(check3 != -1){
    cout<<"Other grid points have "<<check3<<" support grid points."<<endl;
    if(avg_closest_support_spacing != 1000.0 and mainindex_for_max_closest_support_spacing >= 0 and mainindex_for_min_closest_support_spacing >= 0){
      cout<<"Average distance from all (polar and non-polar) main grid points to closest supporting grid point: "<<fixed<<setprecision(1)<<setw(5)<<avg_closest_support_spacing<<" km."<<endl;
      cout<<"Minimum distance from the non-polar main grid point to closest supporting grid point: "<<min_closest_support_spacing<<" km, which occurs at latitude "<<grid.lat.at(mainindex_for_min_closest_support_spacing)<<"."<<endl;
      cout<<"Maximum distance from the non-polar main grid point to closest supporting grid point: "<<max_closest_support_spacing<<" km, which occurs at latitude "<<grid.lat.at(mainindex_for_max_closest_support_spacing)<<"."<<endl;
      cout<<"Average distance from all (polar and non-polar) main grid points to farthest supporting grid point: "<<fixed<<setprecision(1)<<setw(5)<<avg_farthest_support_spacing<<" km."<<endl;
      cout<<"Minimum distance from the non-polar main grid point to farthest supporting grid point: "<<min_farthest_support_spacing<<" km, which occurs at latitude "<<grid.lat.at(mainindex_for_min_farthest_support_spacing)<<"."<<endl;
      cout<<"Maximum distance from the non-polar main grid point to farthest supporting grid point: "<<max_farthest_support_spacing<<" km, which occurs at latitude "<<grid.lat.at(mainindex_for_max_farthest_support_spacing)<<"."<<endl;
    }
  }

  cout<<"Supporting grid is finished."<<endl;
}

grid_s define_region(grid_options_s &region_options,
                     grid_s &grid,
                     int verbose){
  /**********************************************************************
  Purpose: This function defines separate regions in which the inversion
          process will be performed separately.  It also finds out which
          grid points are in each region - either "center" grid points
          or "edge" grid points.  NOTE: It's important that "center" grid
          points be listed first!

          In order to speed up repeated runs with the same parameters,
          I've built in a cache that contains the results of previous
          grids.  The filenames of these cache files are encoded with
          the region_options and grid.options used to create them,
          so searching for a file that has matching options is
          straightforward.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          int verbose - 1/0 turns on/off messages.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  grid_s region;
  long long i,j;//counters.
  double current_lat,current_lon;//temp vars - speed up outside loop.
  double current_lat_halfwidth,current_lon_halfwidth;//Temp vars.
  long long smallest_k;//stores index of nearest topo point.
  //These vars are used to search the cache folder for matching regions.
  DIR *cacheDIR;
  struct dirent *cacheDirEnt;
  //These variables are used to load a matching region from cache.
  FILE *in_fp;
  double onelat,onelon;
  //These variables are used to store a new cache file.
  string newfile = region_options.cachefolder;
  string filename;//stores JUST the filename of cache file (not the path).
  char s[max_length];
  FILE *new_fp;
  //The following variables are used to identify grid points in each region.
  vector<long long> temp_vector_long;//Used to push_back vector of vecs

  char* f_return;//Used to stop fgets return value warning.

  //Copy region_options to region for future reference.
  region.options = region_options;

  //Region boundaries are always identical to grid. Stored
  //only to avoid sending grid structure to region functions.
  region.options.boundaries[0] = grid.options.boundaries[0];
  region.options.boundaries[1] = grid.options.boundaries[1];
  region.options.boundaries[2] = grid.options.boundaries[2];
  region.options.boundaries[3] = grid.options.boundaries[3];

  //Need to do this in case define_region() called twice in a row.
  grid.chosen_for_region.clear();
  grid.chosen_for_region.resize(grid.lat.size(),0);//debug!

  //Automatic cap sizing can alter these variables.  Back them up!
  region.options.original_cap_size = region.options.cap_size;
  region.options.original_lat_spacing = region.options.lat_spacing;
  region.options.original_lon_spacing = region.options.lon_spacing;

  //Search for previously computed regions in the cache, but only if
  //noload == 0 and the grid isn't custom.
  if(region.options.noload == 0 and grid.options.type != 0){

    //Check to see if the cache contains a file with the same options
    //as the ones loaded in region.options.  If so, set i=1.
    i=0;//Init as 0 because it hasn't found matching region in cache yet.

    //First, figure out exactly what filename we're looking for by
    //creating a filename that corresponds to currently selected options.
    filename = create_region_cache_filename(region.options,grid.options);
    //cout<<"Desired region cache filename: "<<filename<<endl;

    //Open the cache folder.
    cacheDIR = opendir(region.options.cachefolder.c_str());

    //Examine each file, see if it contains the desired region.
    cacheDirEnt = readdir(cacheDIR);
    while(cacheDirEnt != NULL && i == 0){
      //If this filename matches desired reg. options, set i=1 to exit loop.
      if(strcmp(filename.c_str(),cacheDirEnt->d_name)==0) i = 1;

      //cout<<"Found file: "<<cacheDirEnt->d_name<<endl;
      cacheDirEnt = readdir(cacheDIR);
    }

    // Release the open folder.
    closedir(cacheDIR);

    //If matching file was found, load it and skip rest of function.
    if(i == 1){
      if(verbose) cout<<"Loading region from cache..."<<endl;
      //Prepend directory to filename, then open.
      newfile.append(filename);
      in_fp = fopen(newfile.c_str(),"r");
      //Load region.lat.size(), store as smallest_k temporarily.
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &smallest_k);
      //Load the real lat spacing- after the autoselect code if that engaged
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &onelat);
      region.options.lat_spacing = onelat;
      //Load the real lon spacing- after the autoselect code if that engaged
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &onelon);
      region.options.lon_spacing = onelon;
      //Load the real cap size- after the autoselect code if that engaged
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lf", &onelon);
      region.options.cap_size = onelon;
      //Use region.lat.size() to resize vectors.
      region.all_grid_indices.resize(smallest_k);
      region.center_grid_indices.resize(smallest_k);
      region.edge_grid_indices.resize(smallest_k);
      region.lat.resize(smallest_k);
      region.lon.resize(smallest_k);
      region.lat_halfwidth.resize(smallest_k);
      region.lon_halfwidth.resize(smallest_k);
      //Load the number of different lats.
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &smallest_k);
      //Use # of diff lats to resize vector.
      region.different_lats.resize(smallest_k);
      //Read in different lats.
      for(j=0;j<(long long)region.different_lats.size();j++){
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf", &onelat);
        region.different_lats.at(j) = onelat;
      }
      //Load the second header line separately because it contains no data.
      f_return=fgets(s,max_length,in_fp);
      //Load each line of data as a string, parse into numeric values,
      //then add each number to its respective vector.
      for(i=0;i<(long long)region.lat.size();i++){
        //Ignore the first line, it's just "New Region:"
        f_return=fgets(s,max_length,in_fp);
        //Now read the coords, elevation and halfwidth.
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf %lf %lf %lf", &onelat, &onelon, &current_lat_halfwidth, &current_lon_halfwidth);
        region.lat.at(i)=onelat;
        region.lon.at(i)=onelon;
        region.lat_halfwidth.at(i)=current_lat_halfwidth;
        region.lon_halfwidth.at(i)=current_lon_halfwidth;
        //Read number of center grid indices:
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lld", &smallest_k);
        region.center_grid_indices.at(i).resize(smallest_k);
        //Read every center grid index:
        for(j=0;j<(long long)region.center_grid_indices.at(i).size();j++){
          f_return=fgets(s,max_length,in_fp);
          sscanf(s,"%lld", &smallest_k);
          region.center_grid_indices.at(i).at(j) = smallest_k;
          region.all_grid_indices.at(i).push_back(smallest_k);
        }
        //Read number of edge grid indices:
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lld", &smallest_k);
        //If there are edge bins, read every edge grid index:
        if(smallest_k > 0){
          region.edge_grid_indices.at(i).resize(smallest_k);
          for(j=0;j<(long long)region.edge_grid_indices.at(i).size();j++){
            f_return=fgets(s,max_length,in_fp);
            sscanf(s,"%lld", &smallest_k);
            region.edge_grid_indices.at(i).at(j) = smallest_k;
            region.all_grid_indices.at(i).push_back(smallest_k);
          }
        }
      }
      fclose(in_fp);
      if(verbose) cout<<endl<<"Finished.  "<<region.lat.size()<<" regions loaded."<<endl;

      //Skip the loading code using a (gasp!) goto function.
      goto end_of_region_loading;
    }
  }

  if(region.options.type == 0){
    if(verbose) cout<<"Region code isn't being used."<<endl;
  }
  else{
    if(verbose) cout<<"Defining type "<<region.options.type<<" regions..."<<endl;
  }

  //If the region cap_size is set to auto area selection (-1.0), use code
  //copied from define_grid type 2 to set the cap_size so cap has the same
  //area as nearby points.  The lat and lon spacings are tweaked as well.
  //Region type 3 does its own autosizing, type 0 doesn't use regions,
  //and type 2 is global.
  if(region.options.cap_size == -1.0 and region.options.type != 0 and region.options.type != 3 and region.options.type != 2){
    autosize_region_cap(region);
  }//End of region cap size auto-set.

  //If region needs to be computed, layout points in manner described
  //by "type".
  switch(region.options.type){
    default: cout<<"!!!!WARNING!!!!!! Region type "<<region.options.type<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 0:{ //No regions at all!
      region.lat.clear();//Don't bother with others, lat is all that matters.
      //Skip the loading code using a (gasp!) goto function.
      goto end_of_region_loading;
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{ //"Pseudo-equal area" region with region over north pole.
             //max_lat must be 90.

      //Use grid code to distribute region latlons.
      calculate_region_as_grid(region);

      //Search for center/edge grid points that lie within each region.
      search_for_grid_pts_in_region(region, grid, verbose);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //Case 2 - Entire grid covered by a single region.  Used in cases where
    //         I want a global solution, but functions like
    //         create_least_squares() require region code to save and load
    //         least squares cache subfolders.  (And possibly other code
    //         requires regions to be used.)
    //////////////////////////////////////////////////////////////////////
    case 2:{ //Entire grid covered by a single region.

      //Define cap on north pole to be the ONLY region point.
      current_lat = 90.0;
      current_lon = 180.0;
      region.lat.push_back(current_lat);
      region.lon.push_back(current_lon);

      //This lon_halfwidth won't be used, but needs to be here.
      region.lon_halfwidth.push_back(180.0);

      //Since this is type 2, the cap_size needs to be over-ridden to
      //cover the entire grid.  This is definitely overkill, but it just
      //needs to have this value while grid points are being found. After
      //that, it will be set to 0.0 so that all type 2 regions with the
      //same underlying grid will be the same.
      region.options.cap_size = twoPi*planet_avg_radius;

      //Over-ride irrelevant variables so that all type 2 regions with the
      //same underlying grid have exactly the same options.  This will help
      //the least squares cache algorithm retrieve a matching set of least
      //squares matrices.
      region.options.lat_spacing = 0.0;
      region.options.lon_spacing = 0.0;
      region.options.overlap_distance = 0.0;

      //For the pole, lat_halfwidth is actually the radius in degrees.
      region.lat_halfwidth.push_back(region.options.cap_size*km2deg);

      //Search for center/edge grid points that lie within this region.
      search_for_grid_pts_in_region(region, grid, verbose);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 3:{//Same as 1, shifted so gr.op.specified_lat/lon has a pt on it.

      //Use grid snapping code to distribute region latlons.
      snap_region_to_point(region);

      //Search for center/edge grid points that lie within each region.
      search_for_grid_pts_in_region(region, grid, verbose);

      break;
    }
  }//End of switch-case.

  if(verbose) cout<<"Finished.  "<<region.lat.size()<<" regions defined."<<endl;

  //If this is a type 2 region, set it to 0.0 so that all type 2
  //regions with the same underlying grid will be the same.
  if(region.options.type == 2){
    region.options.cap_size = 0.0;
  }

  //After creating regions, save them in the cache so they can be
  //retrieved more quickly the next time this same region is requested.
  //But only if the grid isn't custom.
  if(grid.options.type != 0){
    filename=create_region_cache_filename(region.options,grid.options);
    newfile.append(filename);
    if(verbose) cout<<"Writing region to cache file: "<<newfile<<endl;
    new_fp = fopen(newfile.c_str(),"w");
    fprintf(new_fp,"%02lld\n",(long long)region.lat.size());
    fprintf(new_fp,"%20.12f\n",region.options.lat_spacing);//After autosize
    fprintf(new_fp,"%20.12f\n",region.options.lon_spacing);//After autosize
    fprintf(new_fp,"%20.12f\n",region.options.cap_size);//After autosize
    fprintf(new_fp,"%02lld\n",(long long)region.different_lats.size());
    //Then write each "different lat" to the file.
    for(j=0;j<(long long)region.different_lats.size();j++){
      fprintf(new_fp,"%20.12f\n",region.different_lats.at(j));
    }
    //Now record positions of all region points.
    fprintf(new_fp,"            Latitude            Longitude       Lat Halfwidth       Lon Halfwidth\n");
    for(i=0;i<(long long)region.lat.size();i++){
      fprintf(new_fp,"New Region:\n");
      fprintf(new_fp,"%20.12f%21.12f%20.12f%20.12f\n",region.lat.at(i),region.lon.at(i),region.lat_halfwidth.at(i),region.lon_halfwidth.at(i));
      //Now record the vectors region.(all_grid_indices, center_grid_indices, edge_grid_indices).
      //Start w "centers" which will be read into "all" AND center vectors.
      fprintf(new_fp,"%02lld\n",(long long)region.center_grid_indices.at(i).size());
      for(j=0;j<(long long)region.center_grid_indices.at(i).size();j++){
        fprintf(new_fp,"%02lld\n",region.center_grid_indices.at(i).at(j));
      }
      //Now for the edges:
      fprintf(new_fp,"%02lld\n",(long long)region.edge_grid_indices.at(i).size());
      for(j=0;j<(long long)region.edge_grid_indices.at(i).size();j++){
        fprintf(new_fp,"%02lld\n",region.edge_grid_indices.at(i).at(j));
      }
    }
    fclose(new_fp);
    if(verbose) cout<<"Finished."<<endl;
  }

  end_of_region_loading://Successful loading code jumps to this point.

  //Only print details if regions were loaded or calculated.
  if(region.lat.size() > 0){
    //Print details of regions whether loaded or calculated from scratch.
    if(verbose){
      for(i=0;i<(long long)region.lat.size();i++){
        cout<<"Region #"<<i+1<<" (lat: "<<setw(7)<<setprecision(2)<<region.lat.at(i)<<", lon: "<<setprecision(2)<<region.lon.at(i)<<") has "<<region.center_grid_indices.at(i).size()<<"/"<<region.edge_grid_indices.at(i).size()<<" center/edge grid pts."<<endl;
      }
    }

    //Briefly describe autosizing, or lack thereof, but only for types that use it.
    if(region.options.type != 2 and region.options.type != 0){
      if(region.options.original_cap_size == -1.0){
        cout<<"Original region called for lat_spacing "<<fixed<<setprecision(2)<<setw(8)<<region.options.original_lat_spacing<<" km, lon_spacing "<<region.options.original_lon_spacing<<" km, cap_size "<<region.options.original_cap_size<<" km, while autosizing gave the final region lat_spacing "<<region.options.lat_spacing<<" km, lon_spacing "<<region.options.lon_spacing<<" km, cap_size "<<region.options.cap_size<<" km."<<endl;
        cout<<"lat_spacing change: "<<fixed<<setprecision(2)<<100*(region.options.lat_spacing/region.options.original_lat_spacing-1)<<"%, lon_spacing change: "<<100*(region.options.lon_spacing/region.options.original_lon_spacing-1)<<"%."<<endl;
      }
      else{
        cout<<"Region called for (and got- no autosizing) lat_spacing "<<fixed<<setprecision(1)<<setw(7)<<region.options.original_lat_spacing<<" km, lon_spacing "<<region.options.original_lon_spacing<<" km, cap_size "<<fixed<<setprecision(1)<<setw(7)<<region.options.original_cap_size<<" km."<<endl;
        if(region.options.original_cap_size != region.options.cap_size or region.options.original_lat_spacing != region.options.lat_spacing or region.options.original_lon_spacing != region.options.lon_spacing)
          cout<<"!!!!WARNING!!! SHOULD NEVER SEE THIS KDS872NFY273HFJD8"<<endl;
      }
    }
  }
  return region;
}

void calculate_region_as_grid(grid_s &region){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  grid_s grid; //Temporary grid.

  grid.options  = region.options;

  //Now use the regionified temp grid to lay out the points for the regions.
  northern_latlons(grid);

  //After grid lays out points, copy all relevant information to region.
  region.lat = grid.lat;
  region.lon = grid.lon;
  region.lat_halfwidth = grid.lat_halfwidth;
  region.lon_halfwidth = grid.lon_halfwidth;
  region.different_lats = grid.different_lats;
}

void search_for_grid_pts_in_region(grid_s &region, grid_s &grid, int verbose){
  /**********************************************************************
  Purpose: This function searches for center and edge grid points in each
          region.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          int verbose - 1/0 turns on/off messages.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long j,l,counter;//big counters.
  double current_angle;//Search for nearest topo pt, store ang dist. here.
  double smallest_angle = 1000.0;//Start at high value (in radians).

  //The following vars are used to identify grid points in each region.
  int newregion;//set to 1 once per region.
  vector<long long> temp_vector_long;//Used to push_back vector of vectors

  double overlap_angle;//overlap_distance converted to an angle for speed.

  long long index;//Used to correct missed or double-counted grid pts.

  //Initialize variables for notification function for time remaining.
  notification_s notification;
  //1st notification occurs after two loops, but it usually won't print
  //anything because not enough total_time will have passed (first_interval).
  //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
  notification.interval=2;
  //Desired min and max intervals in seconds b/w notifications.
  notification.first_interval=10;
  notification.min_interval=60, notification.max_interval=90;
  //notification.min_interval=30, notification.max_interval=90;
  notification.start_time=-1; notification.current_time=-1;
  notification.previous_time=-1;//Needs to be initialized
  notification.prefix = "Regions getting grid pts";

  notification.starting_index = 0;
  notification.partial_saves_enabled = 0;

  //Used to size notification intervals.
  notification.previous_i=notification.starting_index;
  notification.num_loops = (long long)region.lat.size()-notification.starting_index;
  //notification.interval will not be allowed to go above this number
  //so that at LEAST a certain number of messages are seen.
  notification.max_notif_interval=(long long)(notification.num_loops/4);

  //Annoyingly, these need to be defined! Argh!
  least_squares_s dummy_ls;
  input_s dummy_input;
  results_s dummy_ar;

  notification.start_time = time(NULL);

  for(l=0;l<(long long)region.lat.size();l++){
    notify(notification,l,dummy_ls,dummy_input,dummy_ar);
    //This new region point needs its own entry in
    //vectors all_grid_indices, center_grid_indices,
    //and edge_grid_indices.
    region.all_grid_indices.push_back(temp_vector_long);
    region.center_grid_indices.push_back(temp_vector_long);
    region.edge_grid_indices.push_back(temp_vector_long);
    //Add center grid points first.
    newregion = 1;
    //cout<<"Region "<<l<<" has lat "<<region.lat.at(l)<<" and lon "<<region.lon.at(l)<<endl;
    for(counter=0;counter<(long long)grid.lat.size();counter++){
      if(is_pt_in_bin(region,grid.lat.at(counter),grid.lon.at(counter),l,newregion) == 1){
        //cout<<"Grid pt "<<counter<<" at lat "<<grid.lat.at(counter)<<" and lon "<<grid.lon.at(counter)<<" is the "<<region.all_grid_indices.at(l).size()<<" pt in region "<<l<<endl;
        region.center_grid_indices.at(l).push_back(counter);
        grid.chosen_for_region.at(counter)++;//debug!
      }//End of "if this point is within bin boundaries" statement.
    }//End of loop through grid points.
  }//End of loop through regions.

  //Now that the "easy" grid points are done, time to catch the
  //few points that fall on the line between regions, so they're not
  //caught by "is_pt_in_bin".  For these, use spherical_dist().
  vector<long long>::iterator icgi;//Used to search center_grid_indices.
  for(counter=0;counter<(long long)grid.lat.size();counter++){
    //First, worry about grid points that aren't in any region
    if(grid.chosen_for_region.at(counter) == 0){
      index = grid_index_via_distance(region, grid.lat.at(counter),grid.lon.at(counter), 0);

      cout<<"Grid pt at "<<grid.lat.at(counter)<<", "<<grid.lon.at(counter)<<" was chosen by "<<grid.chosen_for_region.at(counter)<<" regions using fast code, but was closest to region #"<<index+1<<". "<<endl;
      region.center_grid_indices.at(index).push_back(counter);
      grid.chosen_for_region.at(counter) = 1;
    }//Next, worry about grid pt chosen by >1 region.
    //Assume that if they're chosen already, the mask is off or they're in ocean.
    else if(grid.chosen_for_region.at(counter) > 1){
      //Find the region index closest to grid point.
      index = grid_index_via_distance(region, grid.lat.at(counter),grid.lon.at(counter), 0);

      //Then loop through all region indices, and any region that
      //contains this grid point but ISN'T the closest region
      //has this grid point erased from its central_grid_indices.
      for(l=0;l<(long long)region.lat.size();l++){
        icgi = find(region.center_grid_indices.at(l).begin(),region.center_grid_indices.at(l).end(),counter);
        if(l != index and icgi != region.center_grid_indices.at(l).end()){
          cout<<"Grid pt at "<<grid.lat.at(counter)<<", "<<grid.lon.at(counter)<<" was chosen by region #"<<l+1<<" but was closer to region #"<<index+1<<". "<<endl;
         grid.chosen_for_region.at(counter)--;
         region.center_grid_indices.at(l).erase(icgi);
        }//End of "if this region ISN'T closest but claims this grid point.
      }//End of loop through regions.
    }//If this grid pt was chosen by >1 region.
  }//End of loop through grid points.

  //Only add edge points if region.options.overlap_distance > 0.0
  if(region.options.overlap_distance > 0.0){
    //Convert overlap_distance to the equivalent angle.
    overlap_angle = region.options.overlap_distance/planet_avg_radius;
    for(l=0;l<(long long)region.lat.size();l++){
      //Now, loop through all grid points and add those that aren't
      //center points but are below overlap_angle: i.e. edge pts.
      for(j=0;j<(long long)grid.lat.size();j++){
        if(count(region.center_grid_indices.at(l).begin(),region.center_grid_indices.at(l).end(),(long long)j) == 0){
          //Now loop through all center grid points and find the one
          //that's closest.  Store distance in "smallest_angle".
          smallest_angle = 1000.0;//Needs to start out large for each grid pt.
          for(counter=0;counter<(long long)region.center_grid_indices.at(l).size();counter++){
            current_angle = spherical_distance(grid.lat.at(j),grid.lon.at(j), grid.lat.at(region.center_grid_indices.at(l).at(counter)), grid.lon.at(region.center_grid_indices.at(l).at(counter)));
            if(current_angle < smallest_angle){
              smallest_angle = current_angle;
            }
          }//End of loop through center grid points.

          //If this point is close enough to a center pt to be an edge pt:
          if(smallest_angle < overlap_angle){
            region.edge_grid_indices.at(l).push_back(j);
          }//End of "if this is an edge point" statement.
        }//End of "if this grid point ISN'T a center grid point.
      }//End of loop through all grid points.
    }//End of loop through regions.
  }//End of "if overlap_distance > 0" statement.

  //Now that center and edge grid points are finalized, create the
  //vector with all indices in a row.
  for(l=0;l<(long long)region.lat.size();l++){
    for(j=0;j<(long long)region.center_grid_indices.at(l).size();j++){
      region.all_grid_indices.at(l).push_back(region.center_grid_indices.at(l).at(j));
    }
    for(j=0;j<(long long)region.edge_grid_indices.at(l).size();j++){
      region.all_grid_indices.at(l).push_back(region.edge_grid_indices.at(l).at(j));
    }
  }

  notification.end_time = time(NULL);
  notification.time_total=notification.end_time-notification.start_time;
  cout<<notification.prefix<<" took "
    <<seconds_to_string(notification.time_total)<<endl;

  //Check to see that all center grid points have been identified as
  //being in one and only one region!
  long long k=0;//num of non-counted grid pts.
  l=0;//num of double (or more)-counted grid pts.
  for(j=0;j<(long long)grid.lat.size();j++){
    if(grid.chosen_for_region.at(j) == 0) k++;
    if(grid.chosen_for_region.at(j) >= 2) l++;
  }
  if(verbose) cout<<"Total number of grid points: "<<grid.lat.size()<<endl;
  if(verbose) cout<<"Number of grid points chosen by exactly 1 region: "<<count(grid.chosen_for_region.begin(),grid.chosen_for_region.end(),(long long)1)<<endl;
  if(k != 0) cout<<"!!!WARNING!!! Number of non-counted grid points: "<<k<<endl;
  if(l != 0) cout<<"!!!WARNING!!! Number of double (or more)-counted grid pts: "<<l<<endl;
  //If double/ non-counted gr pts exist, list them and which regions claim them.
  if(l>0 or k>0){
    //Loop through grid points.
    for(j=0;j<(long long)grid.lat.size();j++){
      if(grid.chosen_for_region.at(j) == 0){
        cout<<"Grid lat: "<<grid.lat.at(j)<<" lon: "<<grid.lon.at(j)<<" was chosen "<<grid.chosen_for_region.at(j)<<" times."<<endl;
      }
      //Only bother with double-counted (or more) grid points.
      if(grid.chosen_for_region.at(j) > 1){
        cout<<"Grid lat: "<<grid.lat.at(j)<<" lon: "<<grid.lon.at(j)<<" was chosen "<<grid.chosen_for_region.at(j)<<" times."<<endl;
        //Loop through regions to see which regions claim this gr pt.
        for(counter=0;counter<(long long)region.lat.size();counter++){
          if(count(region.center_grid_indices.at(counter).begin(),region.center_grid_indices.at(counter).end(),(long long)j) > 0){
            cout<<"Region #"<<counter+1<<" (lat: "<<region.lat.at(counter)<<" lon: "<<region.lon.at(counter)<<") claims previous grid point."<<endl;
          }
        }
      }//Only bother printing if this grid point was counted >1 times.
    }//End of loop through all grid points.
  }//End of "if double (or more) counted pts found"
}

void load_input(input_s &input, grid_s &grid, grid_s &region){
  /**********************************************************************
  Purpose: This function loads the input data. It can also load the
          input file after being combined with the orbit files from Yuan.
          The epoch (center of timespan) is subtracted from all time
          values in the series in order to reduce numerical errors due to
          very large time values and to stop secular trend from biasing
          constant as much. It's stored in "input.epoch".
  
  load_input() runs differently depending on "region.lat.size()":
  
  ///////////////////////////////////////////////////////////////////////////
  If region.lat.size()==0:
  
  Short version: Load all GRACE acceleration data directly into "input".
  
  Long version:
  
  Read the input data from disk, store it in a structure called input.
  The input structure includes millions of datapoints. Each point has
  time, lat, lon, relative accel and "ascending" recorded. It MAY also
  contain the orbit files for each satellite for ease of use, even
  though technically that's not input data.
  
  ///////////////////////////////////////////////////////////////////////////
  If region.lat.size() != 0:
  
  Short version: Do not try to load the entire set of GRACE acceleration
                 data into memory.  Instead, initialize a vector called
                 input.binindices which will be used to load data in
                 each spatial region using load_regional_input()
  
  Long version:

  Examine the input cache for records of array of llongs "which bin is this
  input point in" recorded for each input data point: ~10MB of data.
  If found, load.  If not found, read input file sequentially (but don't
  try to load the entire thing!) and create the array described above
  using currently defined grid. Then, save this array to a cache file.
  This array will be used to efficiently figure out if a given input pt
  lies within a given region, and thus whether a input point should be
  loaded for analysis in a given region.
  
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //In case input structure isn't empty.
  clear_input(input);

  double onelat,onelon,oneaccel,onexA,onexB,oneyA,oneyB,onezA,onezB;
  long long onetime,i,j,k,l;
  int oneascending;
  char s[max_length];
  string basefolder(gaiafolder());
  string file;
  //The following variables were added when region code was written.
  int newbin;
  //These variables are used to load a matching input from cache.
  FILE *in_fp;
  int matching_cache_found=0;
  //These variables are used to store a new cache file.
  string newfile = input.options.cachefolder;
  string filename;//stores JUST the filename of cache file (not the path).

  //debug
  vector <int> debug;

  char* f_return;//Used to stop fgets return value warning.

  //If no regions are requested, execute old code.
  if(region.lat.size()==0){

    basefolder.append(input.options.basefolder);
    file = basefolder;
    file.append(input.options.filename);

    //Open input file for loading.
    in_fp = fopen(file.c_str(),"r");

    //Warn if file doesn't open correctly.
    if(in_fp == NULL) cout << "The input data file, " << file
                            << ", failed to open."<<endl;
    else cout << "Loading input data from file "
              << input.options.filename << "..."<<endl;

    //Load the first header line separately because it contains no data.
    f_return=fgets(s,max_length,in_fp);

    input.time.clear(); //Not necessary, just being paranoid.
  
    //Load each line of data as a string, parse into numeric values,
    //then add each number to its respective vector.
    //Normal while statement:
    while(fgets(s,max_length,in_fp)!=NULL){
      if(input.options.no_orbit_data == 1){
        sscanf(s,ACCEL_READ_FORMAT, &onetime, &onelat, &onelon, &oneaccel, &oneascending);
      }
      else{
        sscanf(s,ORBIT_READ_FORMAT, &onetime, &onelat, &onelon, &oneaccel, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);
        input.xA.push_back(onexA);
        input.xB.push_back(onexB);
        input.yA.push_back(oneyA);
        input.yB.push_back(oneyB);
        input.zA.push_back(onezA);
        input.zB.push_back(onezB);
      }
      if(input.time.size() == 0) input.first_time = onetime;
      input.time.push_back(onetime);
      input.lat.push_back(onelat);
      if(fabs(onelon - 360.0) < tolerance) onelon = 0;
      input.lon.push_back(onelon);
      input.accel.push_back(oneaccel);
      input.ascending.push_back(oneascending);
    }
    fclose(in_fp);
    
    //After the entire file is read, record onetime - temp variable from
    //file reading- as input.last_time.  This is correct because onetime
    //isn't used anywhere else, and is only reset by the sscanf function
    //so it should still be exactly the same as the last input time.
    input.last_time = onetime;

    //Based on constant vs. trend anti-correlation observed 2008-04-04,
    //seems like a good idea to do this:
    input.epoch = (long long)((double)(input.last_time - input.first_time)/2.0) + input.first_time;

    //Subtract epoch from all times.
    for(long long ii=0;ii<(long long)input.time.size();ii++) input.time.at(ii) -= input.epoch;

    cout << "Finished loading "<<input.time.size()<<" data points."<<endl;
  }//end of "if region.lat.size() == 0" statement.
  //Now calculate (or load from cache) "binindices" vector.
  else{

    //If not using custom grid, try to load input.binindices from cache.
    if(grid.options.type != 0){
      //Note that because there is already a "input.options.noload"
      //which controls loading the entire input file (or not),
      //there isn't a way to disable loading input.binindices from cache!
      //First, figure out exactly what filename we're looking for by
      //creating a filename that corresponds to currently selected options.
      filename = create_input_cache_filename(input.options,grid.options);

      //If a matching cache file is found, load the input structure from it.
      search_input_cache_for_binindices(input,filename,matching_cache_found);
      //If found, skip the loading code using a (gasp!) goto function.
      if(matching_cache_found != 0) goto end_of_input_loading;
    }

    //If no matching cache file is found, need to calculate binindices.

    basefolder.append(input.options.basefolder);
    file = basefolder;
    file.append(input.options.filename);

    //Open input file for binning.
    in_fp = fopen(file.c_str(),"r");

    //Warn if file doesn't open correctly.
    if(in_fp == NULL) cout << "The input data file, " << file
                            << ", failed to open."<<endl;
    else cout << "Binning input data from file "
              << input.options.filename << "..."<<endl;

    //Initialize variables for notification function for time remaining.
    notification_s notification;
    notification.prefix="Input binning";

    notification.start_time = time(NULL);

    //Load the first header line separately because it contains no data.
    f_return=fgets(s,max_length,in_fp);

    //Load the first line so that the epoch time (1st time in the input)
    //can be saved on its own and subtracted from all other time values-
    //though with region code this subtraction occurs later in another fnctn.
    f_return=fgets(s,max_length,in_fp);

    //If orbit data is included in input file:
    if(input.options.no_orbit_data == 0){
      //cout<<"orbit data sscanf statement reads: "<<s<<endl;
      sscanf(s,ORBIT_READ_FORMAT, &onetime, &onelat, &onelon, &oneaccel, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);
    }
    else{//If orbit data is not included in input file:
      //cout<<"no orbit data sscanf statement reads: "<<s<<endl;
      sscanf(s,ACCEL_READ_FORMAT, &onetime, &onelat, &onelon,
             &oneaccel, &oneascending);
    }
    debug.push_back(0);
    input.first_time = onetime;
    //cout<<"input.first_time: "<<input.first_time<<endl;
    input.time.push_back(onetime);//debug!
    input.lat.push_back(onelat);
    if(fabs(onelon - 360.0) < tolerance) onelon = 0;
    input.lon.push_back(onelon);
    //Load each line of data as a string, parse into numeric values,
    //then add to the input structure as a temporary storage place
    //until "input_points_at_once" have been read, then record which
    //bin each of these input points is in.
    long long input_points_at_once = 100000;
    //"k" stores number of input points read, resets at input_points_at_once
    k = 1;
    //"l" stores total number of input points read- doesn't reset.
    l = 1;
    long long missed_bins=0;//Keeps track of how many input pts weren't binned.
    long long doubled_bins=0;//Keeps track of how many input pts binned > once.
    int verbose=0;
    while(fgets(s,max_length,in_fp)!=NULL and k<=input_points_at_once){
      //If orbit data is included in input file:
      if(input.options.no_orbit_data == 0){
        sscanf(s,ORBIT_READ_FORMAT, &onetime, &onelat, &onelon, &oneaccel, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);
      }
      else{//If orbit data is not included in input file:
        sscanf(s,ACCEL_READ_FORMAT, &onetime, &onelat, &onelon,
               &oneaccel, &oneascending);
      }
      input.lat.push_back(onelat);
      if(fabs(onelon - 360.0) < tolerance) onelon = 0;
      input.lon.push_back(onelon);
      input.time.push_back(onetime);//debug!
      debug.push_back(0);
      l++; k++;//Another input point read.

      //Once this outer while loop has read enough input points, figure out
      //which bin they fall into.  The binning algorithm only runs once
      //"enough" input points have been read because it's computationally
      //efficient to put the grid loop on the outside of the binning
      //algorithm.  This makes the bin boundaries get called grid.lat.size()
      //times rather than input.time.size()*grid.lat.size() times- a
      //significant speed up.
      //THIS WILL BE OBSOLETE IF/WHEN BIN BOUNDARIES ARE SAVED IN THE CACHE!!!
      //DOWNSIDE: GRID STRUCT WILL BE LARGER, AND FUTURE TEMPORALLY PARALLEL
      //CODE WILL NEED 1 COPY OF THE GRID PER CPU (RIGHT?) PLUS, IT'S NOT NEEDED
      //IN LEAST SQUARES MATRIX CREATION, SO SAVING BNDRIES WOULDN'T SPEED THAT UP!!
      if(k==input_points_at_once){
        cout<<"Binning "<<input_points_at_once<<" input points - "<<(double)l*100.0/6000000.0 <<"% done if st.np==6M."<<endl;
        //Reset k so the input file will continue to be read after this.
        k=0;
        //Resize the input.binindices vectors so it has entries for
        //all the input points just read.  Set all entries to -1
        //so that any input point that hasn't been binned is obvious.
        input.binindices.resize(l,-1);
        //Loop through bins to find out which bin these input points are in.
        for(i=0;i<(long long)grid.lat.size();i++){
          //cout<<"Binning input data in grid #"<<i+1<<" with lat: "<<grid.lat.at(i)<<" and lon: "<<grid.lon.at(i)<<endl;
          //Set newbin to 1 so that bin boundaries
          //are recalculated in is_pt_in_bin
          newbin=1;

          //Loop through input data, record which bin each falls into.
          for(j=0;j<(long long)input.time.size();j++){
            if(is_pt_in_bin(grid, input.lat.at(j), input.lon.at(j), i, newbin) == 1){
              //This value is complicated because of the method I'm
              //using to read the input file.  The binindices index
              //needs to be the index of the input point- not in the current
              //input.lat array (that's temporary!)- but instead it needs
              //to be the index in the original input file.
              //So: a total of l input points have been read.
              //But, we're cycling through the last
              //"input_num_points_at_once" points, so subtract that.
              //Then add the counter in the input loop and this should
              //be the correct index to which to save "i", which is the
              //actual bin index for this input point.
              input.binindices.at(l-input_points_at_once+j) = i;
              debug.at(l-input_points_at_once+j)++;
              //If the distance from the input point to the bin center
              //is above the value it "should" be, print a set of warning
              //messages.  The value it should be is the lat_spacing
              //(assuming lon_spacing is the same) divided by 2, then
              //multiplied by sqrt(2) (because the point could be in the
              //corner of the bin, which make the distance a hypotenuse).
              //I've found that I need to multiply this distance by 1.32,
              //either because my equal area code fiddles with the distances
              //or because of spherical geometry issues...
              //if(planet_avg_radius*spherical_distance(input.lat.at(j),input.lon.at(j),grid.lat.at(i),grid.lon.at(i)) > grid.options.lat_spacing*1.32/sqrt(2)){
                //cout<<"input pt: "<<l-input_points_at_once+j<<" lat: "<<input.lat.at(j)<<" lon: "<<input.lon.at(j)<<" in bin lat: "<<grid.lat.at(i)<<" lon: "<<grid.lon.at(i)<<endl;
                //cout<<"Distance from bin: "<< planet_avg_radius*spherical_distance(input.lat.at(j),input.lon.at(j),grid.lat.at(i),grid.lon.at(i))<<endl;
              //}
            }
          }//End of loop through (temp) input points.
        }//End of loop through grid points.

        //Now that I've binned the "easy" points, it's time to catch the
        //few points that fall on the line between bins, so they're not
        //caught by "is_pt_in_bin".  For these, use spherical_dist().
        for(j=0;j<(long long)input.time.size();j++){
          //Only bother with input points that haven't been binned.
          if(input.binindices.at(l-input_points_at_once+j) == -1){
            if(verbose) cout<<"input pt at "<<input.lat.at(j)<<", "<<input.lon.at(j)<<" was chosen by "<<j<<" bins using fast code. ";
            input.binindices.at(l-input_points_at_once+j) = grid_index_via_distance(grid, input.lat.at(j),input.lon.at(j), verbose);
            debug.at(l-input_points_at_once+j)++;
            missed_bins++;
          }
          //Or, if this point was placed in >1 bin, find the closer one.
          else if(debug.at(l-input_points_at_once+j)>1){
            if(verbose) cout<<"input pt at "<<input.lat.at(j)<<", "<<input.lon.at(j)<<" was chosen by "<<j<<" bins using fast code. ";
            input.binindices.at(l-input_points_at_once+j) = grid_index_via_distance(grid, input.lat.at(j),input.lon.at(j), verbose);
            debug.at(l-input_points_at_once+j)=1;//Not double-binned now.
            doubled_bins++;
          }
        }//End of loop through (temp) input points.*/

        //Clear input data from these last temporary points.
        input.lat.clear();
        input.lon.clear();
        input.time.clear();//debug!
      }//End of "if k==input_points_at_once" statement.
    }//End of while loop.

    if((long long)input.time.size() == 0){
      cout <<"No leftover data points at end of input file."<<endl;
      fclose(in_fp);
      //Clear input data one last time.
      input.lat.clear();
      input.lon.clear();
      input.time.clear();//debug!
    }
    else{
      cout<<"Binning "<<input.time.size()<<" leftover data points at end of input file."<<endl;

      //This variable is no longer needed, and the final leftovers loop
      //needs it redefined.
      input_points_at_once = input.time.size();

      //Resize the input.binindices vectors so it has entries for
      //all the input points just read.  Set all new entries to -1
      //so that any input point that hasn't been binned is obvious.
      input.binindices.resize(l,-1);

      for(i=0;i<(long long)grid.lat.size();i++){
        //Set newbin to 1 so that bin boundaries
        //are recalculated in is_pt_in_bin
        newbin=1;

        //Loop through input data, record which bin each falls into.
        for(j=0;j<(long long)input.time.size();j++){
          if(is_pt_in_bin(grid, input.lat.at(j), input.lon.at(j), i, newbin) == 1){
            //See above for explanation of this line.
            input.binindices.at(l-input_points_at_once+j) = i;
            debug.at(l-input_points_at_once+j)++;
          }
        }//End of loop through (temp) input points.
      }//End of loop through grid points.

      //Now that I've binned the "easy" points, it's time to catch the
      //few points that fall on the line between bins, so they're not
      //caught by "is_pt_in_bin".  For these, use spherical_dist().
      for(j=0;j<(long long)input.time.size();j++){
        //Only bother with input points that haven't been binned.
        if(input.binindices.at(l-input_points_at_once+j) == -1){
          if(verbose) cout<<"input pt at "<<input.lat.at(j)<<", "<<input.lon.at(j)<<" was chosen by "<<j<<" bins using fast code. ";
          input.binindices.at(l-input_points_at_once+j) = grid_index_via_distance(grid, input.lat.at(j),input.lon.at(j), verbose);
          debug.at(l-input_points_at_once+j)++;
          missed_bins++;
        }
        //Or, if this point was placed in >1 bin, find the closer one.
        else if(debug.at(l-input_points_at_once+j)>1){
          if(verbose) cout<<"input pt at "<<input.lat.at(j)<<", "<<input.lon.at(j)<<" was chosen by "<<j<<" bins using fast code. ";
          input.binindices.at(l-input_points_at_once+j) = grid_index_via_distance(grid, input.lat.at(j),input.lon.at(j), verbose);
          debug.at(l-input_points_at_once+j)=1;//Not double-binned anymore.
          doubled_bins++;
        }
      }//End of loop through (temp) input points.*/

      //Clear input data one last time.
      input.lat.clear();
      input.lon.clear();
      input.time.clear();//debug!

      fclose(in_fp);
    }

    notification.end_time = time(NULL);
    notification.time_total=notification.end_time-notification.start_time;
    cout<<notification.prefix<<" took "
        <<seconds_to_string(notification.time_total)<<endl;

    cout<<"Number of input points that weren't binned the first time: "<<missed_bins<<endl;
    cout<<"Number of input points that were binned twice (or more) the first time: "<<doubled_bins<<endl;

    //The debug vector has the same length as binindices and is incremented
    //every time that input point is binned.  It's also reset when
    //a input point is found to be binned 2x/3x/etc by finding which bin is
    //closer, so double/triple/etc binning should still give "1".
    for(j=0;j<(long long)debug.size();j++){
      if(debug.at(j) > 1) cout<<"!!!WARNING!!! debug.at("<<j<<"): "<<debug.at(j)<<endl;
    }
    for(j=0;j<(long long)input.binindices.size();j++){
      if(input.binindices.at(j) == -1) cout<<"!!!WARNING!!! input.binindices.at("<<j<<"): "<<input.binindices.at(j)<<endl;
    }

    //After the entire file is read, record onetime - temp variable from
    //file reading- as input.last_time.  This is correct because onetime
    //isn't used anywhere else, and is only reset by the sscanf function
    //so it should still be exactly the same as the last input time.
    input.last_time = onetime;

    //Based on constant vs. trend anti-correlation observed 2008-04-04,
    //seems like a good idea to do this:
    input.epoch = (long long)((double)(input.last_time - input.first_time)/2.0) + input.first_time;

    //Even if input_split==0, this is still needed.
    input.verification_points.clear();
    input.verification_points.resize(input.binindices.size(),0);

    //If not using custom grid, save input.binindices to cache.
    if(grid.options.type != 0){
      filename=create_input_cache_filename(input.options,grid.options);
      newfile.append(filename);
      cout<<"Writing input.binindices to cache file: "<<newfile<<endl;
      in_fp = fopen(newfile.c_str(),"w");
      //First line is input.binindices.size()
      fprintf(in_fp,"%lld\n",(long long)input.binindices.size());
      //Second line is input.epoch.
      fprintf(in_fp,"%lld\n",input.epoch);
      //Third line is input.first_time.
      fprintf(in_fp,"%lld\n",input.first_time);
      //Fourth line is input.last_time.
      fprintf(in_fp,"%lld\n",input.last_time);
      //Now, write entire input.binindices vector to the file.
      for(j=0;j<(long long)input.binindices.size();j++){
        fprintf(in_fp,"%lld\n",input.binindices.at(j));
      }
      fclose(in_fp);
    }
    cout << "Finished binning "<<input.binindices.size()<<" input points."<<endl;
  }//End of region.lat.size() != 0 else statement, which is binindices code.
  end_of_input_loading:

  //Important to see these messages even if input is loaded from cache.
  cout<<"First time: "<<setw(12)<<input.first_time<<", which is "<<sec2cal(input.first_time)<<endl;
  cout<<"Last time:  "<<setw(12)<<input.last_time<<", which is "<<sec2cal(input.last_time)<<endl;
  cout<<"Duration:   "<<setw(12)<<seconds_to_string(input.last_time-input.first_time)<<endl;
  cout<<"Epoch time: "<<setw(12)<<input.epoch<<", which is "<<sec2cal(input.epoch)<<endl;
}

input_s split_input(input_s &input, grid_s &region){
  /**********************************************************************
  Purpose: This function designates some points in the input data to be
           "verification" points. They're not used in the inversion, and
           are only used to verify the resulting output afterwards.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  input_s unused_input;
  long long i;

  //Copy input.options to unused_input for future reference.
  unused_input.options = input.options;

  //Copy other variables to unused_input just in case.
  unused_input.epoch = input.epoch;
  unused_input.first_time = input.first_time;
  unused_input.last_time = input.last_time;

  //If no regions are requested, remove verification pts from input vectors
  //and place them in unused_input vectors.
  if(region.lat.size()==0){
    if(input.options.chunk >= (long long)input.time.size()) cout<<"WARNING!!! Chosen value of chunk for split_input is too large for input.time vector!!"<<endl;
    //Different methods of splitting the input require different approaches.
    switch(input.options.split_input){
      default: cout<<"!!!!WARNING!!!!!! split_input "<<input.options.split_input<<" isn't recognized."<<endl;
      case 1:{//Designate last "chunk" points to be verification points.
        //Start at the end of the input, push_back those elements to
        //unused_input vectors.  Then pop_back the elements to remove
        //them from the input vectors.  At end, use reverse to reverse
        //unused_input- because they will be reversed after this procedure
        for(i=0;i<input.options.chunk;i++){
          //Copy points from the end of input to unused_input.
          unused_input.time.push_back(input.time.back());
          unused_input.lat.push_back(input.lat.back());
          unused_input.lon.push_back(input.lon.back());
          unused_input.accel.push_back(input.accel.back());
          unused_input.ascending.push_back(input.ascending.back());
          if(input.options.no_orbit_data == 0){
            unused_input.xA.push_back(input.xA.back());
            unused_input.xB.push_back(input.xB.back());
            unused_input.yA.push_back(input.yA.back());
            unused_input.yB.push_back(input.yB.back());
            unused_input.zA.push_back(input.zA.back());
            unused_input.zB.push_back(input.zB.back());
          }//Commands for input_and_orbit files only.
          //Remove verification points from back of input vectors.
          input.time.pop_back();
          input.lat.pop_back();
          input.lon.pop_back();
          input.accel.pop_back();
          input.ascending.pop_back();
          if(input.options.no_orbit_data == 0){
            input.xA.pop_back();
            input.xB.pop_back();
            input.yA.pop_back();
            input.yB.pop_back();
            input.zA.pop_back();
            input.zB.pop_back();
          }//Commands for input_and_orbit files only.
        }//End of chunk loop.

        //Reverse unused_input vectors- they were added in reverse order.
        reverse(unused_input.time.begin(),unused_input.time.end());
        reverse(unused_input.lat.begin(),unused_input.lat.end());
        reverse(unused_input.lon.begin(),unused_input.lon.end());
        reverse(unused_input.accel.begin(),unused_input.accel.end());
        reverse(unused_input.ascending.begin(),unused_input.ascending.end());
        if(input.options.no_orbit_data == 0){
          reverse(unused_input.xA.begin(),unused_input.xA.end());
          reverse(unused_input.xB.begin(),unused_input.xB.end());
          reverse(unused_input.yA.begin(),unused_input.yA.end());
          reverse(unused_input.yB.begin(),unused_input.yB.end());
          reverse(unused_input.zA.begin(),unused_input.zA.end());
          reverse(unused_input.zB.begin(),unused_input.zB.end());
        }//Commands for input_and_orbit files only.
        cout<<"First inversion time: "<<setw(12)<<input.time.front()+input.epoch<<", which is "<<sec2cal(input.time.front()+input.epoch)<<endl;
        cout<<"Last inversion time:  "<<setw(12)<<input.time.back()+input.epoch<<", which is "<<sec2cal(input.time.back()+input.epoch)<<endl;
        cout<<"Inversion duration:   "<<setw(12)<<seconds_to_string(input.time.back()-input.time.front())<<endl;
        break;
      }
      case 2:{//Designate first "chunk" points to be verification points,
              //after a set of "buffer" points used in the inversion.
        long long end_index = input.options.chunk+input.options.buffer;
        long long temp1=-1,temp2=-1;
        if(end_index >= (long long)input.time.size()) cout<<"WARNING!!! Chosen value of chunk+buffer for split_input is too large for input.time vector!!"<<endl;
        cout<<"Split_input: "<<fixed<<setprecision(2)<<100.0*input.options.buffer/(double)input.time.size()<<"% inversion buffer at beginning, followed by a "<<100.0*input.options.chunk/(double)input.time.size()<<"% verification segment, followed by a "<<100.0*((double)input.time.size() - (double)end_index)/(double)input.time.size()<<"% inversion segment."<<endl;
        if(input.options.buffer>0){
          temp1 = 0; temp2= input.options.buffer-1;
          cout<<"First inversion buffer time: "<<setw(12)<<input.time.at(temp1)+input.epoch<<", which is "<<sec2cal(input.time.at(temp1)+input.epoch)<<endl;
          cout<<"Last inversion buffer time:  "<<setw(12)<<input.time.at(temp2)+input.epoch<<", which is "<<sec2cal(input.time.at(temp2)+input.epoch)<<endl;
          cout<<"Inversion buffer duration:   "<<setw(12)<<seconds_to_string(input.time.at(temp2)-input.time.at(temp1))<<endl;
        }
        temp1 = input.options.buffer; temp2= end_index-1;
        cout<<"First verification segment time: "<<setw(12)<<input.time.at(temp1)+input.epoch<<", which is "<<sec2cal(input.time.at(temp1)+input.epoch)<<endl;
        cout<<"Last verification segment time:  "<<setw(12)<<input.time.at(temp2)+input.epoch<<", which is "<<sec2cal(input.time.at(temp2)+input.epoch)<<endl;
        cout<<"Verification segment duration:   "<<setw(12)<<seconds_to_string(input.time.at(temp2)-input.time.at(temp1))<<endl;
        if(end_index < (long long)input.time.size()-1){
          temp1 = end_index; temp2= (long long)input.time.size()-1;
          cout<<"First inversion segment time: "<<setw(12)<<input.time.at(temp1)+input.epoch<<", which is "<<sec2cal(input.time.at(temp1)+input.epoch)<<endl;
          cout<<"Last inversion segment time:  "<<setw(12)<<input.time.at(temp2)+input.epoch<<", which is "<<sec2cal(input.time.at(temp2)+input.epoch)<<endl;
          cout<<"Inversion segment duration:   "<<setw(12)<<seconds_to_string(input.time.at(temp2)-input.time.at(temp1))<<endl;
        }
        //Start at beginning of the input, push_back those elements to
        //unused_input vectors.  Then erase those elements to remove
        //them from the input vectors.
        for(i=input.options.buffer;i<end_index;i++){
          //Copy points from the beginning of input to unused_input.
          unused_input.time.push_back(input.time.at(i));
          unused_input.lat.push_back(input.lat.at(i));
          unused_input.lon.push_back(input.lon.at(i));
          unused_input.accel.push_back(input.accel.at(i));
          unused_input.ascending.push_back(input.ascending.at(i));
          if(input.options.no_orbit_data == 0){
            unused_input.xA.push_back(input.xA.at(i));
            unused_input.xB.push_back(input.xB.at(i));
            unused_input.yA.push_back(input.yA.at(i));
            unused_input.yB.push_back(input.yB.at(i));
            unused_input.zA.push_back(input.zA.at(i));
            unused_input.zB.push_back(input.zB.at(i));
          }//Commands for input_and_orbit files only.
        }//End of chunk loop.
        //Remove verification points from front of input vectors.
        input.time.erase(input.time.begin()+input.options.buffer,input.time.begin()+end_index);
        input.lat.erase(input.lat.begin()+input.options.buffer,input.lat.begin()+end_index);
        input.lon.erase(input.lon.begin()+input.options.buffer,input.lon.begin()+end_index);
        input.accel.erase(input.accel.begin()+input.options.buffer,input.accel.begin()+end_index);
        input.ascending.erase(input.ascending.begin()+input.options.buffer,input.ascending.begin()+end_index);
        if(input.options.no_orbit_data == 0){
          input.xA.erase(input.xA.begin()+input.options.buffer,input.xA.begin()+end_index);
          input.xB.erase(input.xB.begin()+input.options.buffer,input.xB.begin()+end_index);
          input.yA.erase(input.yA.begin()+input.options.buffer,input.yA.begin()+end_index);
          input.yB.erase(input.yB.begin()+input.options.buffer,input.yB.begin()+end_index);
          input.zA.erase(input.zA.begin()+input.options.buffer,input.zA.begin()+end_index);
          input.zB.erase(input.zB.begin()+input.options.buffer,input.zB.begin()+end_index);
        }//Commands for input_and_orbit files only.
        break;
      }
    }//End of switch-case-break.

    cout<<"Finished separating "<<unused_input.time.size()<<" input points for use in verification later."<<endl<<input.time.size()<<" input points left in input structure to be used in the inversion."<<endl;
  }//End of "if region.lat.size() == 0" statement.
  else{//Region code- just creates verification_points vector.
    if(input.options.chunk >= (long long)input.binindices.size()) cout<<"WARNING!!! Chosen value of chunk for split_input is too large for input.binindices vector!!"<<endl;

    //Different methods of splitting the input require diff. approaches.
    switch(input.options.split_input){
      default: cout<<"!!!!WARNING!!!!!! split_input "<<input.options.split_input<<" isn't recognized."<<endl;
      case 1:{//Designate last "chunk" points to be verification points.
        input.verification_points.clear();
        input.verification_points.resize(input.binindices.size(),0);

        for(i=0;i<input.options.chunk;i++) input.verification_points.at((long long)input.verification_points.size()-1-i)=1;

        break;
      }
      case 2:{//Designate first "chunk" points after buffer to be verification points.
        input.verification_points.clear();
        input.verification_points.resize(input.binindices.size(),0);

        long long end_index = input.options.chunk+input.options.buffer;
        for(i=input.options.buffer;i<end_index;i++) input.verification_points.at(i)=1;

        break;
      }
    }//End of switch-case-break.
    cout<<"Finished designating "<<count(input.verification_points.begin(),input.verification_points.end(),(long long)1)<<" input points for use in verification later."<<endl<<count(input.verification_points.begin(),input.verification_points.end(),(long long)0)<<" input points will be used in the inversion."<<endl;

  }//End of region.lat.size() != 0 else statement, which is region code.

  return unused_input;
}

void build_analysis_region_list(grid_s &grid, grid_s &region){
  /**********************************************************************
  Purpose: This function determines which regions will be analyzed using
           parameters chosen by the user.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  vector<long long> temp;
  input_s temp_input;
  int bin_found=-1,newbin,inregion=0;

  switch(region.options.analysis_list_choice){
    default: cout<<"!!!!WARNING!!!!!! region.options.analysis_list_choice "<<region.options.analysis_list_choice<<" isn't recognized."<<endl;
    case 0:{//Old- start at 0 and run all regions through analysis.
      region.indices_to_analyze.clear();
      for(i=0;i<(long long)region.lat.size();i++) region.indices_to_analyze.push_back(i);
      break;
    }
    case 1:{//1 - Only analyze the region which contains this pt:
      //Now find out which grid bin this pt falls into.  If none, complain!
      for(i=0;i<(long long)grid.lat.size();i++){
        newbin=1;
        if(is_pt_in_bin(grid,region.options.specified_lat,region.options.specified_lon,i,newbin) == 1) bin_found=i;
      }//End of loop through grid points.
      if(bin_found == -1) cout<<"!!!WARNING!!! REQUESTED PT DIDN'T FALL INTO ANY BINS!"<<endl;

      //Now see which region has a CENTER pt that matches bin_found.
      region.indices_to_analyze.clear();
      for(i=0;i<(long long)region.lat.size();i++){
        inregion = count(region.center_grid_indices.at(i).begin(),region.center_grid_indices.at(i).end(),(long long)bin_found);
        if(inregion==1) region.indices_to_analyze.push_back(i);
        inregion=0;//Otherwise all subsequent regions would also be added!
      }
      if(region.indices_to_analyze.size() == 0) cout<<"!!!WARNING!!! REQUESTED PT DIDN'T MATCH ANY REGIONS!"<<endl;
      break;
    }
    case 2:{//2 - Start at end, work towards beginning
      region.indices_to_analyze.clear();
      for(i=(long long)region.lat.size()-1;i>=0;i--) region.indices_to_analyze.push_back(i);
      break;
    }
    case 3:{//3 - Start at end, add until region lat changes.
            //THIS CASE WILL FAIL WHEN SOUTHERN GRID AND REGIONS ARE
            //INTRODUCED- IT SHOULD BE EXPANDED TO MAKE A BAND AROUND THE
            //EQUATOR (LARGEST AREA FOR LOWEST TIME BC OF TRACK DENSITY)
      region.indices_to_analyze.clear();
      for(i=(long long)region.lat.size()-1;i>=0;i--){
        if(region.lat.at(i) == region.lat.back()) region.indices_to_analyze.push_back(i);
      }
      break;
    }
    case 4:{//4 - Analyze regions listed in manual_list ONLY.
      region.indices_to_analyze.clear();
      //Add regions from manual list.
      for(i=0;i<(long long)region.options.manual_list.size();i++){
        if(region.options.manual_list.at(i)>= 0 and region.options.manual_list.at(i) < (long long)region.lat.size()) region.indices_to_analyze.push_back((long long)(region.options.manual_list.at(i)));
        else cout<<"WARNING!  Manual list #"<<i+1<<" contained "<<region.options.manual_list.at(i)<<" but that's not a valid region index- the largest valid region index is "<<region.lat.size()-1<<endl;
      }
      break;
    }
    case 5:{//5 - Analyze regs listed in manual_list 1st, then do others.
      region.indices_to_analyze.clear();

      //Add regions from manual list.
      for(i=0;i<(long long)region.options.manual_list.size();i++){
        if(region.options.manual_list.at(i)>= 0 and region.options.manual_list.at(i) < (long long)region.lat.size()) region.indices_to_analyze.push_back((long long)(region.options.manual_list.at(i)));
        else cout<<"WARNING!  Manual list #"<<i+1<<" contained "<<region.options.manual_list.at(i)<<" but that's not a valid region index- the largest valid region index is "<<region.lat.size()-1<<endl;
      }

      //Now add any region that's not already there.
      for(i=0;i<(long long)region.lat.size();i++) if(count(region.indices_to_analyze.begin(),region.indices_to_analyze.end(),(long long)i) == 0) region.indices_to_analyze.push_back(i);

      break;
    }
  }//End of switch-case-break
}

void perform_analysis(input_s &input,
                      grid_s &grid,
                      ancillary_s &ancillary,
                      model_s &model,
                      results_s &results,
                      plot_options_s &plot_options,
                      grid_s &region,
                      insitu_s &insitu){
  /**********************************************************************
  Purpose: This function uses one of a number of different techniques
           to analyze the input (or model) data given the grid that was
           created in define_grid().
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  results_s temp_results;
  long long i,j,k,l; //Big counters.
  //double maxlat,minlat,maxlon,minlon;//Temp variables- obsolete?
  int newbin;//If 1, tells is_pt_in_bin to recalculate bin boundaries.
  vector<long long> binindices;//Indices to input values in a particular bin.
  char str[max_length];
  //These variables are used in case 3 in a specialized debug
  //routine that extracts datapoints from a particular bin
  //and saves them to a file.
  FILE *new_fp;
  string new_file(gaiafolder());
  char s[max_length];
  //Holds number of times each input point was used in each bin.
  //Hopefully, each entry will be 1.  I've initialized the vector so that
  //it has input.time.size() entries, and each entry is initialized to 0.
  vector<int> data_modified(input.time.size(),0);
  //Used in type 5:
  double rms;//Holds the rms in each bin.

  //Only used with GSL libraries.
  #ifdef GSL_HERE
    //Some analysis types solve y=Hx+e- this is the H matrix.
    h_matrix_s h_matrix;
    
    //Truncated SVD (types 110,111) can be set to truncate based on the
    //median deviation scheme applied to the singular value matrix:
    double median,md;
    
    double weight;//Mult regularization terms by this b4 adding to l_s.A
  #endif

  //Some analysis types solve a modified square problem Ax=b-here's A,b:
  least_squares_s least_squares;

  //Some analysis types need another of these:
  least_squares_s temp_least_squares;

  //Some analysis types don't run unless the number of input points in
  //the input structure for the current region (NOT the current bin) is
  //greater than "minimum_inputpoints".
  long long minimum_inputpoints;
  minimum_inputpoints=100;//Otherwise GSL not here produces warning.

  period_info_s period_info;

  //For analysis type 0, used to make sure that M2 amplitude and phase
  //output files don't produce two entries in parameter, parameter_type,
  //tide vectors.
  vector <string> recognized_titles;
  //Also for type 0, need to load input cache file.
  string filename;
  input_s temp_input;

  //Used in type 10001,2
  double temp_double;

  //Helps standardize each case so they can be copy-pasted.
  long long typesincos;

  //Used with grid_index, which is set to be quiet by default.
  int verbose=0; long long index;

  //Unless changed, all types convert from input.epoch to J2000.
  //MasconFES - type 1003 - is an exception (along with many others).
  if(results.options.inside_load_options==1){
    results.options.current_phase_format = 3;
    results.options.adjust_phases_choice = 3;
  }

  //Perform different types of analyses based on "type".
  switch(results.options.type){
    default: cout<<"!!!!WARNING!!!!!! results.options.type "<<results.options.type<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case -1:{//SKIP ANALYSIS, MAKE PLACEHOLDER OUTPUT W/ 1 CONST PARAM!

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.parameter.push_back(0);
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      plot_options.output_files.resize(1,"skipped_analysis.txt");

      results.titles.push_back("Skipped Analysis");
      results.units.push_back("N/A");

      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 0:{ //Load previously computed output using files referenced by
             //GAIAFOLDER/output/output_filenames.txt.

      //Yes, this type changes params, but that was a special case in
      //load_options and therefore isn't needed here.

      plot_options.inputfolder = "output/";
      load_output(results, plot_options, grid);
      //Most loaded maps are in J2000 format, but check!
      if(results.options.current_phase_format == 1){
        results.options.adjust_phases_choice = -1;
      }
      else cout<<"!!!WARNING!!! Loaded output files have current_phase_format = "<<results.options.current_phase_format<<endl;

      break;
    }

    //////////////////////////////////////////////////////////////////////
    ////////////////////INPUT ONLY - ACCELERATION SPACE///////////////////
    //////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{ //#/bin and region code debugging.

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.parameter.push_back(0);
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      results.titles.push_back("# of datapoints in each bin.");
      results.units.push_back("#/bin");
      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){
        //Clear the binindices vector for each bin.
        binindices.clear();

        //Set newbin to 1 so that bin boundaries
        //are recalculated in is_pt_in_bin
        newbin=1;
        cout<<results.options.prefix;
        printf(": Examining bin #%3lld at (%10.7f,%11.7f).",i+1,grid.lat.at(i),grid.lon.at(i));
        cout<<endl;

        //Loop through input data, extract points that fit in this bin.
        for(j=0;j<(long long)input.time.size();j++){
          //debug!
          //if(input.time.at(j) == 11012000 or input.time.at(j) == 43053010 or input.time.at(j) == 94600485) newbin = 1;
          if(is_pt_in_bin(grid, input.lat.at(j), input.lon.at(j), i, newbin) == 1){
            binindices.push_back(j);//Add current index to binindices.
            data_modified.at(j)++;//Debug - no double-counting allowed!
          }
          //debug region- make the same comparison, but using the maininput
          //structure, which (temporarily!) holds all the information that
          //old style non-region inputs do.
          //have to change j to... something... Ill write a new structure
          //using load_regional_input() in the maininput structure
          //that records the indices (in the huge input vector) of the pts
          //currently extracted for the current region.
          /*else if(is_pt_in_bin(grid, maininput, i, maininput.indices_of_points_in_region.at(j), newbin) == 1){
            cout<<"region code missed maininput["<<maininput.indices_of_points_in_region.at(j)<<"] with lat: "<<maininput.lat.at(maininput.indices_of_points_in_region.at(j))<<" and lon: "<<maininput.lon.at(maininput.indices_of_points_in_region.at(j))<<endl;
            cout<<"maininput2["<<maininput.indices_of_points_in_region2.at(j)<<"] with lat: "<<maininput.lat.at(maininput.indices_of_points_in_region2.at(j))<<" and lon: "<<maininput.lon.at(maininput.indices_of_points_in_region2.at(j))<<endl;
            cout<<"region.input["<<j<<"] with lat: "<<input.lat.at(j)<<input.lat.at(j)<<" and lon: "<<input.lon.at(j)<<endl;
            binindices.push_back(j);*///Add current index to binindices.
            //data_modified.at(j)++;//Debug - no double-counting allowed!
          //}
          //debug - find out if points "not in bin" are still close!
          //I'm doing this because region code for some reason is returning
          //very low values for points in each bin, even though it's loading
          //(almost) exactly the right number of points for region 1.
          //i>0 bc doesn't really work right for cap because its size isn't
          //set by grid.options.lat_spacing
          //else{
          //  if(i>0 and planet_avg_radius*spherical_distance(input.lat.at(j),input.lon.at(j),grid.lat.at(i),grid.lon.at(i)) < grid.options.lat_spacing*1.32/sqrt(2)){
          //    cout<<"input pt: "<<j<<" lat: "<<input.lat.at(j)<<" lon: "<<input.lon.at(j)<<" NOT in bin lat: "<<grid.lat.at(i)<<" lon: "<<grid.lon.at(i)<<endl;
          //    cout<<"Distance from bin: "<< planet_avg_radius*spherical_distance(input.lat.at(j),input.lon.at(j),grid.lat.at(i),grid.lon.at(i))<<endl;
          //  }
          //}
        }

        cout<<results.options.prefix;
        printf(": Found %lld accels in bin %lld (of %lld bins).",(long long)binindices.size(),i+1,(long long)grid.lat.size());
        cout<<endl;

        results.outputs.front().push_back(binindices.size());

        //Debug - write input times, lats, lons, accels to disk if this is
        //a particular bin which shows a difference in the number of
        //input points in the bin when region code is used as opposed to
        //when region code isn't.
        if(1==2 and grid.elev.at(i) == -3710.00){
          FILE *out_fp;
          new_file.append("output/input_in_bin_regions.txt");
          out_fp = fopen(new_file.c_str(),"w");
          //Print lat and lon of this grid point, just in case elevation
          //doesn't uniquely identify the grid point.
          fprintf(out_fp,"Grid lat: %12.4f  lon: %12.4f\n",grid.lat.at(i),grid.lon.at(i));
          //Print the number of input points in this bin.
          fprintf(out_fp,"Number of input points in bin: %12lld\n",(long long)binindices.size());
          fprintf(out_fp,ACCEL_HEADER);
          for(k=0;k<(long long)binindices.size();k++){
            fprintf(out_fp,ACCEL_WRITE_FORMAT,input.time.at(binindices.at(k))+input.epoch, input.lat.at(binindices.at(k)), input.lon.at(binindices.at(k)), input.accel.at(binindices.at(k)), input.ascending.at(binindices.at(k)));
          }
          fclose(out_fp);
          cout<<"saved "<<new_file<<endl;
        }
        //Debug - write input times, lats, lons, accels to disk if this is
        //a particular bin which seems to have an anomalously high RMS on land.
        //This is the output from #/bin for the bin in question:
        //69.4000       86.7857      4.00000000e+00        93.7600
        //This is the output from RMS for the bin in question:
        //69.4000       86.7857      5.55410847e+00        93.7600
        if(1==2 and grid.elev.at(i) == 93.7600){
          FILE *out_fp;
          new_file.append("output/input_in_bin_regions.txt");
          out_fp = fopen(new_file.c_str(),"w");
          //Print lat and lon of this grid point, just in case elevation
          //doesn't uniquely identify the grid point.
          fprintf(out_fp,"Grid lat: %12.4f  lon: %12.4f\n",grid.lat.at(i),grid.lon.at(i));
          //Print the number of input points in this bin.
          fprintf(out_fp,"Number of input points in bin: %12lld\n",(long long)binindices.size());
          fprintf(out_fp,ACCEL_HEADER);
          for(k=0;k<(long long)binindices.size();k++){
            fprintf(out_fp,ACCEL_WRITE_FORMAT,input.time.at(binindices.at(k))+input.epoch, input.lat.at(binindices.at(k)), input.lon.at(binindices.at(k)), input.accel.at(binindices.at(k)), input.ascending.at(binindices.at(k)));
          }
          fclose(out_fp);
          cout<<"saved "<<new_file<<endl;
        }
      }//End of loop through bins.
      //Debug - make sure each input point is only counted once
      if(1==1){
        k=0;//num of non-counted input pts.
        l=0;//num of double-counted input pts.
        for(j=0;j<(long long)input.time.size();j++){
          if(data_modified.at(j) == 0) {
            k++;
            if(1==1) cout<<"lat["<<j<<"]: "<<fixed<<setprecision(6)<<input.lat.at(j)<<" lon: "<<fixed<<setprecision(6)<<input.lon.at(j)<<" wasn't counted."<<endl;
          }
          if(data_modified.at(j) == 2) l++;
          if(data_modified.at(j) > 2) cout<<"data_modified["<<j<<"]: "<<data_modified.at(j)<<endl;
        }
        cout<<"Number of non-counted input points: "<<k<<endl;
        cout<<"Number of double-counted input pts: "<<l<<endl;
      }

      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 2:{ //Compute the RMS in each bin.

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.parameter.push_back(0);
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes the constant term title.
      results.titles.clear();
      results.titles.push_back("RMS");

      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){
        //Clear the binindices vector for each bin.
        binindices.clear();

        //Set newbin to 1 so that bin boundaries
        //are recalculated in is_pt_in_bin
        newbin=1;

        cout<<results.options.prefix;
        printf(": Examining bin #%3lld at (%10.7f,%11.7f).",i+1,grid.lat.at(i),grid.lon.at(i));
        cout<<endl;

        //Loop through input data, extract points that fit in this bin.
        for(j=0;j<(long long)input.time.size();j++){
          if(is_pt_in_bin(grid, input.lat.at(j), input.lon.at(j), i, newbin) == 1){

            binindices.push_back(j);//Add current index to binindices.
          }
        }

        cout<<results.options.prefix;
        printf(": Found %lld accels in bin %lld (of %lld bins).",(long long)binindices.size(),i+1,(long long)grid.lat.size());
        cout<<endl;

        //Record the RMS if any accelerations are present in the bin.
        if(binindices.size() > 0){
          rms = 0.0;
          for(j=0;j<(long long)binindices.size();j++) rms += input.accel.at(binindices.at(j))*input.accel.at(binindices.at(j));
          rms = 1.0e9*sqrt(rms/binindices.size());//"rms" simply held the sum of squares until now.
          results.outputs.front().at(i)=rms;
        }//End of "if any accelerations are present" if statement.
        else results.outputs.front().at(i)=0.0;

      }//End of loop through bins.

      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 3:
    case 4:{ //Fit functions (sines, polynomials, etc) to data
             //in each bin based on parameters passed from gaia
             //and returns the cosine and sine amplitudes.
             //Case 3 - sin, cos coeffs.  Case 4 - amp,phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 4) results.options.sincos = 0;
        else results.options.sincos = 1;
        results.options.uses_least_squares = 0;
        return;
      }

      //Just return zero if the num of input points in the bin 
      //is less than this num to avoid reporting values that
      //are statistically invalid.
      long long min_input_pts_in_bin=200;

      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){
        //Clear the binindices vector for each bin.
        binindices.clear();

        //Set newbin to 1 so that bin boundaries
        //are recalculated in is_pt_in_bin
        newbin=1;

        cout<<results.options.prefix;
        printf(": Examining bin #%3lld at (%10.7f,%11.7f).",i+1,grid.lat.at(i),grid.lon.at(i));
        cout<<endl;

        //Loop through input data, extract points that fit in this bin.
        //k=0;//debug.
        for(j=0;j<(long long)input.time.size();j++){
          //Is the current input point in the current bin?
          if(is_pt_in_bin(grid, input.lat.at(j), input.lon.at(j), i, newbin) == 1){
            binindices.push_back(j);//Add current index to binindices.
          }//End of "if input point is in bin" statement.
        }//End of loop that goes through input file.

        cout<<results.options.prefix;
        printf(": Found %lld accels in bin %lld (of %lld bins).",(long long)binindices.size(),i+1,(long long)grid.lat.size());
        cout<<endl;

        //For debug purposes, extract datapoints that lie in the
        //bin containing the pt. (dlat,dlon) and save them to a file.
        double dlat=54.7, dlon=305.2174;
        long long dindex = grid_index(grid, dlat, dlon, 0);
        if(1==2 and i==dindex){
          cout<<"Writing debug input file for bin with grid.lat= "<<grid.lat.at(i)<<" and grid.lon= "<<grid.lon.at(i)<<endl;
          new_file.append(input.options.basefolder);
          sprintf(str,"input_bin_lat-%5.2f_lon-%6.2f.txt",grid.lat.at(i),grid.lon.at(i));
          new_file.append(str);
          new_fp = fopen(new_file.c_str(),"w");
          fprintf(new_fp,ACCEL_HEADER);
          for(j=0;j<(long long)binindices.size();j++){
            fprintf(new_fp,ACCEL_WRITE_FORMAT,input.time.at(binindices.at(j))+input.epoch, input.lat.at(binindices.at(j)), input.lon.at(binindices.at(j)), input.accel.at(binindices.at(j)), input.ascending.at(binindices.at(j)));
          }
          printf("Finished writing debug input file.\n");
          fclose(new_fp);
        }

        //If the number of data points in this bin is "large enough",
        //solve the normal equations and record the output.
        if((long long)binindices.size() > min_input_pts_in_bin){
          //Requires GSL libraries.
          #ifdef GSL_HERE
            //Record the dimension of b (and first dimension of RECTANGULAR A) here.
            least_squares.dimension = results.options.output_type.size();
            long long rows = binindices.size();//Num of data pts > dimension (i.e. num of params).

            //cout<<"Allocating GSL A matrix with dimensions "<<rows<<", "<<least_squares.dimension<<" and GSL b vector with dimension "<<rows<<endl;
            //Initialize "A" matrix- correct dimensions and populated with 0.0.
            least_squares.A = gsl_matrix_alloc(rows, least_squares.dimension);
            gsl_matrix_set_zero(least_squares.A);

            //Initialize "b" vector- correct dimension and populated with 0.0.
            least_squares.b = gsl_vector_alloc(rows);
            gsl_vector_set_zero(least_squares.b);

            //Define partials of parameters to be estimated
            for(j=0;j<(long long)binindices.size();j++){
              //Multiply by 10^9 to convert to nm/s^2.
              gsl_vector_set(least_squares.b, j, 1E9*input.accel.at(binindices.at(j)));
              l=0;
              for(k=0;k<(long long)results.options.parameter.size();k++){
                //New parameter types should be added here.
                if(results.options.parameter_type.at(k)==0){//Const
                  gsl_matrix_set(least_squares.A, j, l, 1.0);
                  l++;
                }
                else if(results.options.parameter_type.at(k) < 100){
                  //Because pow(±0,y) returns ±∞ and raises the divide-by-zero floating-point exception for y an odd integer.
                  if(input.time.at(binindices.at(j)) != 0) gsl_matrix_set(least_squares.A, j, l, pow(input.time.at(binindices.at(j)),results.options.parameter_type.at(k)));//Partial of n'th polynomial term.
                  else gsl_matrix_set(least_squares.A, j, l, 0.0);//Won't ever return ±∞ or raise an exception.
                  l++;
                }
                else if(results.options.parameter_type.at(k) == 101){
                  gsl_matrix_set(least_squares.A, j, l, cos(results.omegas.at(k)*input.time.at(binindices.at(j))));
                  l++;
                  gsl_matrix_set(least_squares.A, j, l, sin(results.omegas.at(k)*input.time.at(binindices.at(j))));
                  l++;
                }
                else if(results.options.parameter_type.at(k) >= 10000 and results.options.parameter_type.at(k) < 20000){
                  gsl_matrix_set(least_squares.A, j, l, input.arbitrary.y_values.at(k).at(0).at(binindices.at(j)));
                  l++;
                }
                else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.4"<<endl;
              }
            }
            //residual is "e" in b=Ax+e.
            gsl_vector * residual = gsl_vector_alloc(rows);
            gsl_vector * x = gsl_vector_alloc(least_squares.dimension);
            gsl_vector_set_zero(residual);
            //The vector tau contains internal QR decomp information.
            gsl_vector * tau = gsl_vector_alloc(least_squares.dimension);
            gsl_vector_set_zero(tau);
            //Solve normal equations
            //cout<<"Solving using gsl_linalg_QR_decomp() and gsl_linalg_QR_lssolve().  This may take a while..."<<endl;
            gsl_linalg_QR_decomp(least_squares.A, tau);
            gsl_linalg_QR_lssolve(least_squares.A, tau, least_squares.b, x, residual);
            //Record output differently for different parameter types.
            l=0;
            for(k=0;k<(long long)results.options.parameter.size();k++){
              //New parameter types should be added here.
              if(results.options.parameter_type.at(k) < 100){//Polynomial
                results.outputs.at(l).at(i) = gsl_vector_get(x,l)*pow(seconds_in_year,results.options.parameter_type.at(k));//convert from 1/sec^n to 1/year^n
                l++;
              }
              else if(results.options.parameter_type.at(k)==101){//Sinusoid
                switch(results.options.type){
                  default: cout<<"!!!!WARNING!!!!!! results.options.type "<<results.options.type<<" isn't recognized."<<endl;
                  case 3:{
                    results.outputs.at(l).at(i)=gsl_vector_get(x,l);
                    l++;//Save cosine amplitude, increment counter.
                    results.outputs.at(l).at(i)=gsl_vector_get(x,l);
                    l++;//Save sine amplitude, increment counter.
                    break;
                  }
                  case 4:{
                    results.outputs.at(l).at(i) = sqrt(gsl_vector_get(x,l)*gsl_vector_get(x,l)+gsl_vector_get(x,l+1)*gsl_vector_get(x,l+1));
                    //Save amplitude (above), don't increment counter.
                    results.outputs.at(l+1).at(i) = rad2deg*atan2(gsl_vector_get(x,l+1),gsl_vector_get(x,l));
                    l=l+2;//Save phase, increment counter by 2.
                    break;
                  }
                }//End switch-case.
              }//End sinusoids.
              else if(results.options.parameter_type.at(k) >= 10000 and results.options.parameter_type.at(k) < 20000){
                results.outputs.at(l).at(i) = gsl_vector_get(x,l);
                l++;
              }
              else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.5"<<endl;
            }//End of loop that cycles through parameters.
            //Free the space taken up by the various matrices and vectors.
            gsl_matrix_free(least_squares.A);
            gsl_vector_free(least_squares.b);
            gsl_vector_free(x);
            gsl_vector_free(tau);
            gsl_vector_free(residual);
          #else 
            cout<<"Type 3 requires GSL. Search definitions.cpp for GSL_HERE."<<endl;
          #endif
        }
        else{//If not enough data points exist, set all results to 0.
          for(k=0;k<(long long)results.options.output_type.size();k++){
            results.outputs.at(k).at(i) = 0.0;
          }
        }
      }//End of loop that cycles through bins.

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 5:{ //Region "i" has each grid point hold value "i" to identify
             //regions.

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.parameter.push_back(0);
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      sprintf(str,"Regions");
      results.titles.push_back(str);
      results.units.push_back("N/A");
      if(results.options.region_index < 0) cout<<"Analysis type 5 needs to be run with region code!"<<endl;
      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.front().at(i) = results.options.region_index;
      }

      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 6:{ //Each region stores input.time.size() in each
             //center grid point.

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.parameter.push_back(0);
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      sprintf(str,"Number of input points in each region");
      results.titles.push_back(str);
      results.units.push_back("N/A");
      if(results.options.region_index < 0) cout<<"Analysis type 6 needs to be run with region code!"<<endl;
      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.front().at(i) = input.time.size();
      }

      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 7:{ //Make 1 map per region where center pts
             //are 1.0 and ovlp pts are 0.5.

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.parameter.push_back(0);
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //Parameter sizes can't be calculated until define_region() is run.
      //So this type will resize param/param_type here!
      results.options.parameter.clear();
      results.options.parameter_type.clear();
      results.options.parameter.resize(region.lat.size(),0);
      results.options.parameter_type.resize(region.lat.size(),0);
      create_output_vectors(results,input);

      //Now resize the second dimension (have to do this multiple times)
      //so that the second dimension holds all grid points.
      //Note that the first dimension was set in create_output_vectors().
      for(i=0;i<(long long)results.options.output_type.size();i++){
        results.outputs.at(i).resize(grid.lat.size(),0.0);
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      results.titles.resize(region.lat.size());
      results.units.resize(region.lat.size());
      //Loop through regions.
      for(j=0;j<(long long)region.lat.size();j++){
        sprintf(str,"Region #%lld of %lld",j+1,(long long)region.lat.size());
        results.titles.at(j) = str;
        results.units.at(j) = "N/A";
        //Loop through center pts.
        for(i=0;i<(long long)region.center_grid_indices.at(j).size();i++) results.outputs.at(j).at(region.center_grid_indices.at(j).at(i)) = 1;
        //Loop through ovlp pts.
        for(i=0;i<(long long)region.edge_grid_indices.at(j).size();i++) results.outputs.at(j).at(region.edge_grid_indices.at(j).at(i)) = 0.5;
      }

      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 8:
    case 9:{//Evaluate phase of specified tidal constituents every time
            //GRACE flies directly over each bin (only 1st st pt in bin!)
            //Then output:
            //Type 8 -  average phase diff in degrees, rms about mean.
            //Type 9 -  # of phase diffs > phaseparam1, % of ".
            //THIS TYPE REQUIRES REGION CODE SO BININDICES IS AVAILABLE!

      if(results.options.inside_load_options==1){
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      //Usually in degrees, used to control different types.
      double phaseparam1=results.options.lambda;

      if(region.lat.size() == 0)
        cout<<"!!!WARNING!!! THIS TYPE REQUIRES REGION CODE SO BININDICES IS AVAILABLE!!"<<endl;

      //Old binning cases (ex: 3,4) just return zero if the num of
      //input points in the bin is less than this num to avoid reporting
      //values that are statistically invalid. SET REAL LOW FOR NOW!
      long long min_input_pts_in_bin=3;//200

      vector< vector<double> > phases;//phase of each tide for each pass.

      //Used for debug statements- time between passes.
      timespan_conversion_s timespan_conversion;

      //Set each title based on the parameter and parameter type.
      //Remember that even though there are "parameter.size()" parameters,
      //the periods count twice so there should actually be "output_type.size()"
      //titles and units.
      for(k=0;k<(long long)results.options.parameter.size();k++){
        if(results.options.parameter_type.at(k) == 101){
          is_this_period_recognized(results.options.parameter.at(k),period_info);
          strcpy(str,period_info.title.c_str());
          //Set 2 sinusoid titles and units depending on type of analysis.
          switch(results.options.type){
            default: cout<<"!!!!WARNING!!!!!! results.options.type "<<results.options.type<<" isn't recognized."<<endl;
            case 8:{
              //Output map 1.
              results.titles.push_back(str);
              results.titles.back().append(" Avg Phase Diff.");
              results.units.push_back("degrees");
              //Output map 2.
              results.titles.push_back(str);
              results.titles.back().append(" Phase Diff RMS.");
              results.units.push_back("degrees");
              break;
            }
            case 9:{
              //Output map 1.
              results.titles.push_back(str);
              sprintf(s," # Phase Diffs < %.0f deg.",phaseparam1);
              results.titles.back().append(s);
              results.units.push_back("#");
              //Output map 2.
              results.titles.push_back(str);
              sprintf(s," %% Phase Diffs < %.0f deg.",phaseparam1);
              results.titles.back().append(s);
              results.units.push_back("%");
              break;
            }
          }//End switch-case.
        }//End of sinusoid if statement.
        else{
          cout<<"!!!WARNING!!! THIS CODE ONLY MAKES SENSE FOR SINUSOID PARAMS!"<<endl;
          results.titles.push_back("Not a sinusoid term!");
          results.units.push_back("N/A");
        }
      }//End of loop through parameters.

      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){
        cout<<results.options.prefix;
        printf(": Examining bin #%3lld at (%10.7f,%11.7f).",i+1,grid.lat.at(i),grid.lon.at(i));
        cout<<endl;

        //Clear multivector from last bin's phases.
        phases.clear();
        phases.resize(results.options.parameter.size());

        //Is the first input point in the current bin?
        j=0;
        if(input.binindices.at(j)==i){
          //Define phase of parameters (all sines!) at this input time.
          for(k=0;k<(long long)results.options.parameter.size();k++){
            //BE CAREFUL EDITING- J>0 TERMS BELOW NEED TO BE CHANGED TOO!
            phases.at(k).push_back(results.omegas.at(k)*input.time.at(j));
          }
        }//End of "if input point is in bin" statement.

        //Loop through input data, extract points that fit in this bin.
        //but don't count multiple points in a row- only record the first.
        for(j=1;j<(long long)input.time.size();j++){
          //Is the current input point in the current bin?
          //Make sure previous input pt wasn't also in bin!
          if(input.binindices.at(j)==i and input.binindices.at(j-1)!=i){
            //DEBUG
            if(1==2 and phases.front().size() >= 1 and (i==0 or i==(long long)grid.lat.size()-1)){

            //Compare current time to time recovered from "phases", print.
            timespan_conversion.seconds=input.time.at(j) - (long long)(phases.front().back()/results.omegas.front());
            cout<<"Time since last pass ("<<phases.front().size()<<"): "<<sec2human(timespan_conversion)<<endl;
            }

            //Define phase of parameters (all sines!) at this input time.
            for(k=0;k<(long long)results.options.parameter.size();k++){
              //BE CAREFUL EDITING- J=0 TERM ABOVE NEEDS TO BE CHANGED TOO!
              phases.at(k).push_back(results.omegas.at(k)*input.time.at(j));
            }
          }//End of "if input point is in bin" statement.
        }//End of loop that goes through input file.

        cout<<results.options.prefix;
        printf(": Found %lld passes over bin %lld (of %lld bins).",(long long)phases.front().size(),i+1,(long long)grid.lat.size());
        cout<<endl;

        //If the number of data points in this bin is "large enough",
        //calculate and record the output.
        if((long long)phases.front().size() > min_input_pts_in_bin){
          l=0;
          for(k=0;k<(long long)results.options.parameter.size();k++){
            //Calculate mean phase diff.
            double mean_phase_diff = 0.0;
            long long number_less_than_pp1=0;
            for(j=1;j<(long long)phases.at(k).size();j++){
              mean_phase_diff += phases.at(k).at(j) - phases.at(k).at(j-1);
              if(phases.at(k).at(j) - phases.at(k).at(j-1) < deg2rad*phaseparam1)
                number_less_than_pp1++;
            }
            //Divide by number of phase differences.
            mean_phase_diff /= ((double)phases.at(k).size() - 1.0);

            //Calculate RMS of phase diff around mean phase diff.
            double rms_phase_diff = 0.0;
            for(j=1;j<(long long)phases.at(k).size();j++){
              rms_phase_diff += pow(phases.at(k).at(j) - phases.at(k).at(j-1) - mean_phase_diff,2);;
            }
            //Divide by number of phase differences.
            rms_phase_diff /= ((double)phases.at(k).size() - 1.0);
            //Take square root to convert from variance.
            rms_phase_diff = sqrt(rms_phase_diff);

            switch(results.options.type){
              default: cout<<"!!!!WARNING!!!!!! results.options.type "<<results.options.type<<" isn't recognized."<<endl;
              case 8:{
                results.outputs.at(l).at(i) = rad2deg*mean_phase_diff;
                l++;
                results.outputs.at(l).at(i) = rad2deg*rms_phase_diff;
                l++;
                break;
              }
              case 9:{
                results.outputs.at(l).at(i) = number_less_than_pp1;
                l++;
                results.outputs.at(l).at(i) = number_less_than_pp1*100.0/((double)phases.at(k).size() - 1.0);
                l++;
                break;
              }
            }//End switch-case.
          }//End of loop that cycles through parameters.
        }
        else{//If not enough data points exist, set all results to 0.
          for(k=0;k<(long long)results.options.output_type.size();k++)
            results.outputs.at(k).at(i) = 0.0;
        }
      }//End of loop that cycles through bins.
      break;

    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10:{//Evaluate phase of specified tidal constituents every time
             //GRACE flies directly over each bin (only 1st st pt in bin!)
             //Note that the ABSOLUTE phase is being recorded, not the
             //phase difference!
             //Then create a 2D plot for each bin showing how many times
             //each phase was found to have a certain value. The phases
             //are modulo 360 and grouped into chunks of size "lambda".
             //THIS TYPE REQUIRES REGION CODE SO BININDICES IS AVAILABLE!

      if(results.options.inside_load_options==1){
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      double max_phase=360.0;

      //Lambda is used to set the size of the chunks.
      double chunksize=results.options.lambda;
      //If 360 isn't divisible by chunksize, make the chunks larger.
      int numchunks = (int)floor(max_phase/chunksize);
      chunksize= max_phase/(double)numchunks;
      cout<<"Phases are being grouped into "<<numchunks<<" chunks of size "<<chunksize<<" degrees."<<endl;

      if(region.lat.size() == 0) cout<<"!!!WARNING!!! THIS TYPE REQUIRES REGION CODE SO BININDICES IS AVAILABLE!!"<<endl;

      //Override the usual map output.
      results.options.output_choice = 2;

      vector< vector<double> > phases;//phase of each tide for each pass.

      //Used for debug statements- time between passes.
      timespan_conversion_s timespan_conversion;

      //Used to push_back vectors of dbl vectors
      vector<double> temp_v_double;
      //push_back vs of vs of dbl vectors
      vector< vector<double> > temp_vv_double;
      //push_back vs of string vectors
      vector<string> temp_v_string;

      //Just use this code to warn if non-sinusoid parameters are used.
      for(k=0;k<(long long)results.options.parameter.size();k++){
        if(results.options.parameter_type.at(k) != 101){
          cout<<"!!!WARNING!!! THIS CODE ONLY MAKES SENSE FOR SINUSOID PARAMS!"<<endl;
          results.titles.push_back("Not a sinusoid term!");
          results.units.push_back("N/A");
        }
      }//End of loop through parameters.

      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){
        cout<<results.options.prefix;
        printf(": Examining bin #%3lld at (%10.7f,%11.7f).",i+1,grid.lat.at(i),grid.lon.at(i));
        cout<<endl;

        //Clear multivector from last bin's phases.
        phases.clear();
        phases.resize(results.options.parameter.size());

        //Is the first input point in the current bin?
        j=0;
        if(input.binindices.at(j)==i){
          //Define phase of parameters (all sines!) at this input time.
          for(k=0;k<(long long)results.options.parameter.size();k++){
            //BE CAREFUL EDITING- J>0 TERMS BELOW NEED TO BE CHANGED TOO!
            //note that phases are modulo 360.
            //NOTE: input time is relative to 1st pt!
            //NOTE: phases are immediately converted to degrees!
            //NOTE: this line is useless b/c it's ZERO, but I'm keeping it
            //      for consistency with j> 0 below.
            phases.at(k).push_back(fmod(rad2deg*results.omegas.at(k)*(input.time.at(j)-input.time.front()),max_phase));
          }
        }//End of "if input point is in bin" statement.

        //Loop through input data, extract points that fit in this bin.
        //but don't count multiple points in a row- only record the first.
        for(j=1;j<(long long)input.time.size();j++){
          //Is the current input point in the current bin?
          //Make sure previous input pt wasn't also in bin!
          if(input.binindices.at(j)==i and input.binindices.at(j-1)!=i){
            //DEBUG
            if(1==2 and phases.front().size() >= 1 and (i==0 or i==(long long)grid.lat.size()-1)){

              //Compare current time to time from "phases", print.
              timespan_conversion.seconds=input.time.at(j) - (long long)(phases.front().back()/results.omegas.front());
              cout<<"Time since last pass ("<<phases.front().size()<<"): "<<sec2human(timespan_conversion)<<endl;
            }

            //Define phase of parameters (all sines!) at this input time.
            for(k=0;k<(long long)results.options.parameter.size();k++){
              //BE CAREFUL EDITING- J=0 TERM ABOVE NEEDS TO BE CHANGED TOO!
              //note that phases are modulo 360.
              //NOTE: input time is relative to 1st pt!
              //NOTE: phases are immediately converted to degrees!
              phases.at(k).push_back(fmod(rad2deg*results.omegas.at(k)*(input.time.at(j)-input.time.front()),max_phase));
            }
          }//End of "if input point is in bin" statement.
        }//End of loop that goes through input file.

        cout<<results.options.prefix;
        printf(": Found %lld passes over bin %lld (of %lld bins).",(long long)phases.front().size(),i+1,(long long)grid.lat.size());
        cout<<endl;

        //Save misc info for this plot, like title (for polar, other bins).
        if(grid.lat.at(i) == 90.0){

          sprintf(s,"N Pole bin, Diam %.0f km, %lld passes.",2*grid.options.cap_size,(long long)phases.front().size());
        }
        else if(grid.lat.at(i) == -90.0){

          sprintf(s,"S Pole bin, Diam %.0f km, %lld passes.",2*grid.options.cap_size,(long long)phases.front().size());
        }
        else{
          sprintf(s,"Bin (%2.0fN,%3.0fE), E-W %.0fkm, %lld passes.",grid.lat.at(i),grid.lon.at(i),planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i)-grid.lon_halfwidth.at(i),grid.lat.at(i),grid.lon.at(i)+grid.lon_halfwidth.at(i)),(long long)phases.front().size());
        }

        results.xy.titles.push_back(s);
        results.xy.x_units.push_back("Phase (degrees)");
        results.xy.y_units.push_back("#");

        //This is a brand new plot, so push_back using temp vectors/vvs.
        results.xy.x_values.push_back(temp_vv_double);
        results.xy.y_values.push_back(temp_vv_double);
        results.xy.legends.push_back(temp_v_string);

        //This plot has "parameter.size()" lines in it.
        results.xy.x_values.at(i).resize(results.options.parameter.size());
        results.xy.y_values.at(i).resize(results.options.parameter.size());

        //Each line has "numchunks" data points.
        for(j=0;j<(long long)results.options.parameter.size();j++){
          results.xy.x_values.at(i).at(j).resize(numchunks);
          results.xy.y_values.at(i).at(j).resize(numchunks);
        }

        //Use phases vector to figure out how many phases in each chunk.
        long long current_chunk;
        vector< vector <long long> > num_in_chunk;
        num_in_chunk.clear();
        num_in_chunk.resize(results.options.parameter.size());
        //Loop through all parameters.
        for(j=0;j<(long long)results.options.parameter.size();j++){
          //Each param as numchunks entries.
          num_in_chunk.at(j).resize(numchunks,0);
          //Loop through all recorded phases for this parameter.
          for(k=0;k<(long long)phases.at(j).size();k++){
            //What chunk does this phase fall into?
            current_chunk = (long long)floor(phases.at(j).at(k)/chunksize);

            if(current_chunk >= numchunks) cout<<"WARNING! numchunks = "<<numchunks<<", but current_chunk = "<<current_chunk<<"."<<endl;
            //Increment that chunk's num_in_chunk.
            num_in_chunk.at(j).at(current_chunk)++;
          }
        }

        //Loop through params to save output for each line.
        for(j=0;j<(long long)results.options.parameter.size();j++){
          //Legend for this plot and this param is the param's title.
          is_this_period_recognized(results.options.parameter.at(j),period_info);
          results.xy.legends.at(i).push_back(period_info.title);
          //Save individual XY values for this particular param's line.
          for(k=0;k<(long long)numchunks;k++){
            //X-value is the MIDDLE of the chunk.
            results.xy.x_values.at(i).at(j).at(k) = ((double)k+0.5)*chunksize;
            results.xy.y_values.at(i).at(j).at(k) = num_in_chunk.at(j).at(k);
          }
        }

      }//End of loop that cycles through bins.
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 11:
    case 13:{
      //Evaluate RELATIVE phase of specified tidal constituents every time
      //GRACE flies directly over each bin (only 1st st pt in bin!)
      //Note that the RELATIVE phase difference is being recorded, not the
      //absolute phase as in type 10!
      //Then create a 2D plot for each bin showing how many times
      //each phase difference was found to have a certain value. The phases
      //are grouped into chunks of size "lambda" up to 360.
      //THIS TYPE REQUIRES REGION CODE SO BININDICES IS AVAILABLE!
      
      if(results.options.inside_load_options==1){
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      //Case 13 only makes plots for the poles and one at each latitude.

      double max_phase=360.0;

      //Lambda is used to set the size of the chunks.
      double chunksize=results.options.lambda;
      //If 360 isn't divisible by chunksize, make the chunks larger.
      int numchunks = (int)floor(max_phase/chunksize);
      chunksize= max_phase/(double)numchunks;
      cout<<"Phases are being grouped into "<<numchunks<<" chunks of size "<<chunksize<<" degrees."<<endl;

      if(region.lat.size() == 0) cout<<"!!!WARNING!!! THIS TYPE REQUIRES REGION CODE SO BININDICES IS AVAILABLE!!"<<endl;

      //Override the usual map output.
      results.options.output_choice = 2;

      vector< vector<double> > phases;//phase of each tide for each pass.

      //Used for debug statements- time between passes.
      timespan_conversion_s timespan_conversion;

      //Used to push_back vectors of dbl vectors
      vector<double> temp_v_double;
      //push_back vs of vs of dbl vectors
      vector< vector<double> > temp_vv_double;
      //push_back vs of string vectors
      vector<string> temp_v_string;

      //Just use this code to warn if non-sinusoid parameters are used.
      for(k=0;k<(long long)results.options.parameter.size();k++){
        if(results.options.parameter_type.at(k) != 101){
          cout<<"!!!WARNING!!! THIS CODE ONLY MAKES SENSE FOR SINUSOID PARAMS!"<<endl;
          results.titles.push_back("Not a sinusoid term!");
          results.units.push_back("N/A");
        }
      }//End of loop through parameters.

      long long analyze_bin=0,bins_analyzed_index=-1;

      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){

        //Case 13 only uses poles and one per lat.
        if(results.options.type == 11) analyze_bin = 1;
        else if(i == 0) analyze_bin = 1;
        else if(fabs(grid.lat.at(i) - grid.lat.at(i-1)) >= high_tolerance) analyze_bin = 1;
        else analyze_bin = 0;

        if(analyze_bin == 1){
          bins_analyzed_index++;

          cout<<results.options.prefix;
          printf(": Examining bin #%3lld at (%10.7f,%11.7f).",i+1,grid.lat.at(i),grid.lon.at(i));
          cout<<endl;

          //Clear multivector from last bin's phases.
          phases.clear();
          phases.resize(results.options.parameter.size());

          //Is the first input point in the current bin?
          j=0;
          if(input.binindices.at(j)==i){
            //Define phase of parameters (all sines!) at this input time.
            for(k=0;k<(long long)results.options.parameter.size();k++){
              //BE CAREFUL EDITING- J>0 TERMS BELOW NEED TO BE CHANGED TOO!
              //note that phases are NOT modulo 360.
              //NOTE: input time is relative to 1st pt!
              //NOTE: phases are immediately converted to degrees!
              //NOTE: this line is useless b/c it's ZERO, but I'm keeping it
              //      for consistency with j > 0 below.
              phases.at(k).push_back(rad2deg*results.omegas.at(k)*(input.time.at(j)-input.time.front()));
            }
          }//End of "if input point is in bin" statement.

          //Loop through input data, extract points that fit in this bin.
          //but don't count multiple points in a row- only record the first.
          for(j=1;j<(long long)input.time.size();j++){
            //Is the current input point in the current bin?
            //Make sure previous input pt wasn't also in bin!
            if(input.binindices.at(j)==i and input.binindices.at(j-1)!=i){
              //DEBUG
              if(1==2 and phases.front().size() >= 1 and (i==0 or i==(long long)grid.lat.size()-1)){

                //Compare current time to time from "phases", print.
                timespan_conversion.seconds=input.time.at(j) - (long long)(phases.front().back()/results.omegas.front());
                cout<<"Time since last pass ("<<phases.front().size()<<"): "<<sec2human(timespan_conversion)<<endl;
              }

              //Define phase of parameters (all sines!) at this input time.
              for(k=0;k<(long long)results.options.parameter.size();k++){
                //BE CAREFUL EDITING- J=0 TERM ABOVE NEEDS TO BE CHANGED TOO!
                //note that phases are NOT modulo 360.
                //NOTE: input time is relative to 1st pt!
                //NOTE: phases are immediately converted to degrees!
                phases.at(k).push_back(rad2deg*results.omegas.at(k)*(input.time.at(j)-input.time.front()));
              }
            }//End of "if input point is in bin" statement.
          }//End of loop that goes through input file.

          cout<<results.options.prefix;
          printf(": Found %lld passes over bin %lld (of %lld bins).",(long long)phases.front().size(),i+1,(long long)grid.lat.size());
          cout<<endl;

          //Save misc info for this plot, like title (for polar, other bins).
          if(grid.lat.at(i) == 90.0){

            sprintf(s,"N Pole bin, Diam %.0f km, %lld passes.",2*grid.options.cap_size,(long long)phases.front().size());
          }
          else if(grid.lat.at(i) == -90.0){

            sprintf(s,"S Pole bin, Diam %.0f km, %lld passes.",2*grid.options.cap_size,(long long)phases.front().size());
          }
          else{
            sprintf(s,"Bin (%2.0fN,%3.0fE), E-W %.0fkm, %lld passes.",grid.lat.at(i),grid.lon.at(i),planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i)-grid.lon_halfwidth.at(i),grid.lat.at(i),grid.lon.at(i)+grid.lon_halfwidth.at(i)),(long long)phases.front().size());
          }

          results.xy.titles.push_back(s);
          results.xy.x_units.push_back("Phase difference (degrees)");
          results.xy.y_units.push_back("#");

          //This is a brand new plot, so push_back using temp vectors/vvs.
          results.xy.x_values.push_back(temp_vv_double);
          results.xy.y_values.push_back(temp_vv_double);
          results.xy.legends.push_back(temp_v_string);

          //This plot has "parameter.size()" lines in it.
          results.xy.x_values.at(bins_analyzed_index).resize(results.options.parameter.size());
          results.xy.y_values.at(bins_analyzed_index).resize(results.options.parameter.size());

          //Each line has "numchunks" data points.
          for(j=0;j<(long long)results.options.parameter.size();j++){
            results.xy.x_values.at(bins_analyzed_index).at(j).resize(numchunks);
            results.xy.y_values.at(bins_analyzed_index).at(j).resize(numchunks);
          }

          //Use phases vec to figure out how many phase diffs in each chunk.
          long long current_chunk;
          vector< vector <long long> > num_in_chunk;
          num_in_chunk.clear();
          num_in_chunk.resize(results.options.parameter.size());
          //Records how many phase diffs were greater than the maximum (360).
          vector <long long> over_limit;
          over_limit.clear();
          over_limit.resize(results.options.parameter.size());
          //Loop through all parameters.
          for(j=0;j<(long long)results.options.parameter.size();j++){
            //Each param as numchunks entries.
            num_in_chunk.at(j).resize(numchunks,0);
            //Loop through all recorded phases for this parameter.
            for(k=1;k<(long long)phases.at(j).size();k++){
              //What chunk does this phase fall into?
              current_chunk = (long long)floor((phases.at(j).at(k) - phases.at(j).at(k-1))/chunksize);

              //If the index is too big, this phase diff is > max_phase.
              if(current_chunk >= numchunks) over_limit.at(j)++;
              else //Increment that chunk's num_in_chunk.
              num_in_chunk.at(j).at(current_chunk)++;
            }
          }

          //Loop through params to save output for each line.
          for(j=0;j<(long long)results.options.parameter.size();j++){
            //Legend for this plot and this param is the param's title.
            is_this_period_recognized(results.options.parameter.at(j),period_info);
            results.xy.legends.at(bins_analyzed_index).push_back(period_info.title);
            //Save individual XY values for this particular param's line.
            for(k=0;k<(long long)numchunks;k++){
              //X-value is the MIDDLE of the chunk.
              results.xy.x_values.at(bins_analyzed_index).at(j).at(k) = ((double)k+0.5)*chunksize;
              results.xy.y_values.at(bins_analyzed_index).at(j).at(k) = num_in_chunk.at(j).at(k);
            }

            //Output the number of phase diffs above max_phase.
            if(over_limit.at(j) > 0)
              cout<<period_info.title<<" has "<<over_limit.at(j)<<" phase differences above "<<max_phase<<" degrees."<<endl;
          }
        }//End of "should this bin be analyzed?
      }//End of loop that cycles through bins.

      //Unless changed, all types convert from input.epoch to J2000.
      //RELATIVE phases!
      results.options.adjust_phases_choice = -1;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 14:{
      //Plots first "regparamint1" input pts' positions as separate points.

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();
      //And there's no output anyway in this structure.
      results.outputs.clear();

      //Following is supporting grid output code from gaia.
      grid_s input_pts_grid;
      results_s input_pts_results;
      plot_options_s input_pts_plot_options;
      string input_pts_base;

      //Resize arrays, define title and output folder and filename base.
      input_pts_results.outputs.resize(1);//Only 1 output file.
      input_pts_results.titles.push_back("Input points");
      input_pts_plot_options = plot_options;
      input_pts_base="input_pts";
      input_pts_results.options.output_choice=3;

      //Save input point locations and color in mult_out (as double,
      //but write_output() type 3 will write them as long long).
      long long swath_count = 0;//Allows each swath to be colored differently.
      long long swath_threshold=20*60;//Max time in b/w 2 st pts in same swath.
      for(i=0;i<results.options.regparamint1;i++){
        if(i>0)
          if(input.time.at(i) - input.time.at(i-1) >= swath_threshold)
            swath_count++;

        input_pts_grid.lat.push_back(input.lat.at(i));
        input_pts_grid.lon.push_back(input.lon.at(i));
        input_pts_results.outputs.front().push_back(swath_count);
      }
      write_output(input_pts_base, input_pts_results, input_pts_grid, input_pts_plot_options, 1);

      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 15:{
      //Evaluate time difference every time GRACE flies directly over 
      //each bin (only 1st st pt in bin!)
      //Then create a 2D plot for each bin showing how many times
      //each time difference was found to have a certain value. These
      //are grouped into chunks of size "lambda" up to max_time.
      //THIS TYPE REQUIRES REGION CODE SO BININDICES IS AVAILABLE!
      if(results.options.inside_load_options==1){
        results.options.sincos = 1;//Why not? It doesn't matter...
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      //Case 15 only makes plots for the poles and one at each latitude.
      double max_time=15.0;//In hours.

      //Lambda is used to set the size of the chunks, again in hours.
      double chunksize=results.options.lambda;
      //If max_time isn't divisible by chunksize, make the chunks larger.
      int numchunks = (int)floor(max_time/chunksize);
      chunksize= max_time/(double)numchunks;
      cout<<"Time diffs are being grouped into "<<numchunks<<" chunks of size "<<chunksize<<" hours."<<endl;

      if(region.lat.size() == 0) cout<<"!!!WARNING!!! THIS TYPE REQUIRES REGION CODE SO BININDICES IS AVAILABLE!!"<<endl;

      //Override the usual map output.
      results.options.output_choice = 2;

      vector<double> times;//time diff between passes.

      //Used to push_back vectors of dbl vectors
      vector<double> temp_v_double;
      //push_back vs of vs of dbl vectors
      vector< vector<double> > temp_vv_double;
      //push_back vs of string vectors
      vector<string> temp_v_string;

      long long analyze_bin=0,bins_analyzed_index=-1;

      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){

        if(i == 0) analyze_bin = 1;
        else if(fabs(grid.lat.at(i) - grid.lat.at(i-1)) >= high_tolerance) analyze_bin = 1;
        else analyze_bin = 0;

        if(analyze_bin == 1){
          bins_analyzed_index++;

          cout<<results.options.prefix;
          printf(": Examining bin #%3lld at (%10.7f,%11.7f).",i+1,grid.lat.at(i),grid.lon.at(i));
          cout<<endl;

          //Clear vector from last bin's times.
          times.clear();

          //Is the first input point in the current bin?
          j=0;
          if(input.binindices.at(j)==i){
            times.push_back((input.time.at(j)-input.time.front())/3600.0);
          }//End of "if input point is in bin" statement.

          //Loop through input data, extract points that fit in this bin.
          //but don't count multiple points in a row- only record the first.
          for(j=1;j<(long long)input.time.size();j++){
            //Is the current input point in the current bin?
            //Make sure previous input pt wasn't also in bin!
            if(input.binindices.at(j)==i and input.binindices.at(j-1)!=i){
              times.push_back((input.time.at(j)-input.time.front())/3600.0);
            }//End of "if input point is in bin" statement.
          }//End of loop that goes through input file.

          cout<<results.options.prefix;
          printf(": Found %lld passes over bin %lld (of %lld bins).",(long long)times.size(),i+1,(long long)grid.lat.size());
          cout<<endl;

          //Save misc info for this plot, like title (for polar, other bins).
          if(grid.lat.at(i) == 90.0){

            sprintf(s,"N Pole bin, Diam %.0f km, %lld passes.",2*grid.options.cap_size,(long long)times.size());
          }
          else if(grid.lat.at(i) == -90.0){

            sprintf(s,"S Pole bin, Diam %.0f km, %lld passes.",2*grid.options.cap_size,(long long)times.size());
          }
          else{
            sprintf(s,"Bin (%2.0fN,%3.0fE), E-W %.0fkm, %lld passes.",grid.lat.at(i),grid.lon.at(i),planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i)-grid.lon_halfwidth.at(i),grid.lat.at(i),grid.lon.at(i)+grid.lon_halfwidth.at(i)),(long long)times.size());
          }
         
          results.xy.titles.push_back(s);
          results.xy.x_units.push_back("Time difference (hours)");
          results.xy.y_units.push_back("#");

          //This is a brand new plot, so push_back using temp vectors/vvs.
          results.xy.x_values.push_back(temp_vv_double);
          results.xy.y_values.push_back(temp_vv_double);

          //This plot has 1 line in it.
          results.xy.x_values.at(bins_analyzed_index).resize(1);
          results.xy.y_values.at(bins_analyzed_index).resize(1);

          //That line has "numchunks" data points.
          results.xy.x_values.at(bins_analyzed_index).front().resize(numchunks);
          results.xy.y_values.at(bins_analyzed_index).front().resize(numchunks);

          //Use times vec to figure out how many time diffs in each chunk.
          long long current_chunk;
          vector <long long> num_in_chunk;
          num_in_chunk.clear();
          //Records how many time diffs were greater than the maximum.
          long long over_limit=0;
          num_in_chunk.resize(numchunks,0);
          //Loop through all recorded time diffs.
          for(k=1;k<(long long)times.size();k++){
            //What chunk does this time fall into?
            current_chunk = (long long)floor((times.at(k) - times.at(k-1))/chunksize);

            //If the index is too big, this time diff is > max_time.
            if(current_chunk >= numchunks) over_limit++;
            else num_in_chunk.at(current_chunk)++;
          }

          //Save individual XY values for this particular param's line.
          for(k=0;k<(long long)numchunks;k++){
            //X-value is the MIDDLE of the chunk.
            results.xy.x_values.at(bins_analyzed_index).front().at(k) = ((double)k+0.5)*chunksize;
            results.xy.y_values.at(bins_analyzed_index).front().at(k) = num_in_chunk.at(k);
          }
          //Output the number of time diffs above max_time.
          if(over_limit > 0)
            cout<<over_limit<<" time diffs above "<<max_time<<" hours."<<endl;
        }//End of "should this bin be analyzed?
      }//End of loop that cycles through bins.

      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 16:{
      //Plots first "regparamint1" swath accels with color scale.

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();
      //And there's no output anyway in this structure.
      results.outputs.clear();

      grid_s init_grid, accels_grid;
      results_s init_results, accels_results;
      plot_options_s accels_plot_options, all_maps;
      string accels_base;

      //Holds indices of pts in current "swath candidate"
      vector<long> current_swath_indices;

      double delta = input.time.at(1)-input.time.front();
      if(delta > 5) cout<<"Note: delta = "<<delta<<endl;
      
      //Resize arrays, define title and output folder and filename base.
      accels_results.outputs.clear();//Only 1 output file at a time.
      accels_results.outputs.resize(1);//Only 1 output file at a time.
      accels_plot_options = plot_options;
      all_maps = accels_plot_options;
      accels_results.options.output_choice=1;
      accels_results.options.current_phase_format = 0;
      init_results = accels_results;
      
      //Loop until numswaths have been recorded or end of input is reached.
      i=0;//No swaths recorded
      j=1;//Starting on second input entry so that c_s_i has at least 1 entry.
      current_swath_indices.push_back(0);
      while(i<results.options.regparamint1 and j<(long long)input.time.size()){
      
        //Is the next point too far away to be in the same swath, or is there NO next point?
        if(input.time.at(j) - input.time.at(current_swath_indices.back()) > delta or j==(long long)input.time.size()-1){
          //If so, have enough points been found for this to be a swath?
          if((long)current_swath_indices.size() >= 240){
            accels_grid = init_grid;
            accels_results = init_results;
            
            //Save misc info for this plot.
            sprintf(s,"Swath #%3lld",i+1);
            accels_results.titles.push_back(s);
            sprintf(s,"accels_on_map_%03lld",i+1);
            accels_base=s;
            accels_results.units.push_back("m/s^2");
            
            //Save individual XY values for this plot.
            for(k=0;k<(long)current_swath_indices.size();k++){
              accels_grid.lat.push_back(input.lat.at(current_swath_indices.at(k)));
              accels_grid.lon.push_back(input.lon.at(current_swath_indices.at(k)));
              accels_grid.elev.push_back(0);
              accels_results.outputs.front().push_back(input.accel.at(current_swath_indices.at(k)));
            }
            write_output(accels_base, accels_results, accels_grid, accels_plot_options, 0);
            //Save the filenames for output_filenames (after loops).
            for(k=0;k<(long long)accels_plot_options.just_the_filenames.size();k++){
              all_maps.just_the_filenames.push_back(accels_plot_options.just_the_filenames.at(k));
            }
            i++;
          }
          //Either way, the last swath is gonna get cleared.
          current_swath_indices.clear();
          current_swath_indices.push_back(j);
        }
        else{//If index j is in the current swath, add it to the list.
          current_swath_indices.push_back(j);
        }
        j++;//Next input point.
      }//End of loop through input.

      //Create output_filenames with ALL those filenames.
      create_output_filenames(all_maps,accels_results);

      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////INPUT ONLY - MASCON CM SPACE////////////////////
    //////////////////////////////////////////////////////////////////////

    //Requires GSL libraries.
    #ifdef GSL_HERE
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 102:
    case 103:{//Invert input data to pt. masses located at grid centers,
              //using QR decomposition and multiple periods from
              //parameter and parameter_type.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 102) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //"H" matrix is created using results.options.h_matrix_choice.
      h_matrix = create_h_matrix(input,grid,results);

      //Solve y=Hx+e using QR decomposition.
      gsl_vector * y = gsl_vector_alloc(input.time.size());
      gsl_vector * x = gsl_vector_alloc((long long)grid.lat.size()*results.options.output_type.size());
      gsl_vector_set_zero(y);
      gsl_vector_set_zero(x);

      for(i=0;i<(long long)input.time.size();i++){
        gsl_vector_set(y,i,input.accel.at(i));
      }

      //residual is "e" in y=Hx+e.
      gsl_vector * residual = gsl_vector_alloc(input.time.size());
      gsl_vector_set_zero(residual);
      //The vector tau contains internal QR decomp information.
      gsl_vector * tau = gsl_vector_alloc((long long)grid.lat.size()*results.options.output_type.size());
      gsl_vector_set_zero(tau);
      cout<<results.options.prefix<<": Solving y=Hx+e using gsl_linalg_QR_decomp() and gsl_linalg_QR_lssolve().  This may take a while..."<<endl;

      gsl_linalg_QR_decomp(h_matrix.gsl, tau);
      gsl_linalg_QR_lssolve (h_matrix.gsl, tau, y, x, residual);

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Check for output that's too large.
      //Loop through grid points.
      for(i=0;i<(long long)grid.lat.size();i++){
        for(j=0;j<(long long)results.options.output_type.size();j++){
          if(1==1 and results.outputs.at(j).at(i) > 100.0){
            cout<<"!!!SPECIAL GRID POINT - value = "<<results.outputs.at(j).at(i)<<endl;
            cout<<"input.time.size()= "<<input.time.size()<<endl;
            cout<<"grid lat: "<<grid.lat.at(i)<<endl;
            cout<<"grid lon: "<<grid.lon.at(i)<<endl;
          }
        }//End of loop through parameters.
      }//End of loop through grid points.

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(h_matrix.gsl);
      gsl_vector_free(x);
      gsl_vector_free(y);
      gsl_vector_free(tau);
      gsl_vector_free(residual);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 104:
    case 105:{//Invert input data to pt. masses located at grid centers,
              //using LU decompos. and multiple periods from parameter and
              //parameter_type.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 104) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //Matrices for Ax=b are created using results.options.h_matrix_choice.
      least_squares = create_least_squares_in_parallel(input, grid, results);
      gsl_vector * x = gsl_vector_alloc(least_squares.dimension);

      //Solve Ax=b using LU decomposition.
      cout<<results.options.prefix<<": Solving Ax=b using gsl_linalg_LU_decomp() and gsl_linalg_LU_solve()."<<endl;

      int gsl_s;
      gsl_permutation * p = gsl_permutation_alloc(least_squares.dimension);
      gsl_linalg_LU_decomp(least_squares.A, p, &gsl_s);
      gsl_linalg_LU_solve (least_squares.A, p, least_squares.b, x);

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares.A);
      gsl_vector_free(least_squares.b);
      gsl_vector_free(x);
      gsl_permutation_free(p);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 106:
    case 107:{//Invert input data to pt. masses located at grid centers,
              //using SVD and multiple periods from parameter and
              //parameter_type.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 106) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //"H" matrix is created using results.options.h_matrix_choice.
      h_matrix = create_h_matrix(input,grid,results);

      //Solve y=Hx+e using SVD
      gsl_vector * y = gsl_vector_alloc(input.time.size());
      gsl_vector * x = gsl_vector_alloc((long long)grid.lat.size()*results.options.output_type.size());
      gsl_vector * S = gsl_vector_alloc((long long)grid.lat.size()*results.options.output_type.size());
      gsl_vector * temp_vector = gsl_vector_alloc((long long)grid.lat.size()*results.options.output_type.size());
      gsl_matrix * V = gsl_matrix_alloc((long long)grid.lat.size()*results.options.output_type.size(),grid.lat.size()*results.options.output_type.size());
      gsl_matrix * temp_matrix = gsl_matrix_alloc((long long)grid.lat.size()*results.options.output_type.size(),grid.lat.size()*results.options.output_type.size());
      gsl_vector_set_zero(y);
      gsl_vector_set_zero(x);
      gsl_vector_set_zero(temp_vector);
      gsl_vector_set_zero(S);
      gsl_matrix_set_zero(temp_matrix);
      gsl_matrix_set_zero(V);

      for(i=0;i<(long long)input.time.size();i++){
        gsl_vector_set(y,i,input.accel.at(i));
      }

      cout<<results.options.prefix<<": Solving y=Hx+e using gsl_linalg_SV_decomp_mod() and gsl_linalg_SV_solve()."<<endl;

      gsl_linalg_SV_decomp_mod (h_matrix.gsl, temp_matrix, V, S, temp_vector);
      if(results.options.region_index == -1){
        cout << "Maximum singular value = "<<gsl_vector_max(S)<<endl;
        cout << "Minimum singular value = "<<gsl_vector_min(S)<<endl;
      }
      else{
        cout<<results.options.prefix<<": maximum singular value = "<<gsl_vector_max(S)<<endl;
        cout<<results.options.prefix<<": minimum singular value = "<<gsl_vector_min(S)<<endl;
      }

      gsl_linalg_SV_solve (h_matrix.gsl, V, S, y, x);

      //printf ("S = \n");
      //gsl_vector_fprintf (stdout, S, "%g");

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(h_matrix.gsl);
      gsl_vector_free(x);
      gsl_vector_free(y);
      gsl_vector_free(S);
      gsl_vector_free(temp_vector);
      gsl_matrix_free(V);
      gsl_matrix_free(temp_matrix);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 108:
    case 109:{//Invert input data to pt. masses located at grid centers,
              //using SVD after conversion to square problem Ax=b.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 108) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 1;
        return;
      }
      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //Matrices for Ax=b are created using results.options.h_matrix_choice.
      least_squares = create_least_squares_in_parallel(input, grid, results);

      //printf ("b = \n");
      //gsl_vector_fprintf (stdout, least_squares.b, "%g");
      //printf ("A = \n");
      //gsl_matrix_fprintf (stdout, least_squares.A, "%g");

      //Solve Ax=b using SV decomposition.
      gsl_vector * x = gsl_vector_alloc(least_squares.dimension);
      gsl_vector * S = gsl_vector_alloc(least_squares.dimension);
      gsl_matrix * V = gsl_matrix_alloc(least_squares.dimension,least_squares.dimension);
      gsl_vector_set_zero(x);
      gsl_vector_set_zero(S);
      gsl_matrix_set_zero(V);

      cout<<results.options.prefix<<": Solving Ax=b using gsl_linalg_SV_decomp_jacobi() and gsl_linalg_SV_solve()."<<endl;
      gsl_linalg_SV_decomp_jacobi(least_squares.A, V, S);
      if(results.options.region_index == -1){
        cout << "Maximum singular value = "<<gsl_vector_max(S)<<endl;
        cout << "Minimum singular value = "<<gsl_vector_min(S)<<endl;
      }
      else{
        cout<<results.options.prefix<<": maximum singular value = "<<gsl_vector_max(S)<<endl;
        cout<<results.options.prefix<<": minimum singular value = "<<gsl_vector_min(S)<<endl;
      }

      gsl_linalg_SV_solve(least_squares.A, V, S, least_squares.b, x);

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares.A);
      gsl_vector_free(least_squares.b);
      gsl_vector_free(x);
      gsl_vector_free(S);
      gsl_matrix_free(V);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 110:
    case 111:{//Invert input data to pt. masses located at grid centers,
              //using truncated, square SVD using slow, accurate function.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 110) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 1;
        return;
      }
      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //Matrices for Ax=b are created using results.options.h_matrix_choice.
      least_squares = create_least_squares_in_parallel(input, grid, results);

      //Solve Ax=b using SV decomposition.
      gsl_vector * x = gsl_vector_alloc(least_squares.dimension);
      gsl_vector * S = gsl_vector_alloc(least_squares.dimension);
      gsl_matrix * V = gsl_matrix_alloc(least_squares.dimension,least_squares.dimension);
      gsl_vector_set_zero(x);
      gsl_vector_set_zero(S);
      gsl_matrix_set_zero(V);

      cout<<results.options.prefix<<": Solving Ax=b using gsl_linalg_SV_decomp_jacobi() and gsl_linalg_SV_solve()."<<endl;

      gsl_linalg_SV_decomp_jacobi(least_squares.A, V, S);

      //First truncation method.
      //Replace any singular values less than "trunc" with zero.
      //Trunc is calculated by dividing the maximum singular value by...
      double trunc = gsl_vector_max(S)/5.0;
      if(1==1){
        if(results.options.region_index == -1){
          cout << "Maximum singular value = "<<gsl_vector_max(S)<<endl;
          cout << "Minimum singular value = "<<gsl_vector_min(S)<<endl;
        }
        else{
          cout<<results.options.prefix<<": maximum singular value = "<<gsl_vector_max(S)<<endl;
          cout<<results.options.prefix<<": minimum singular value = "<<gsl_vector_min(S)<<endl;
        }
        j=0;
        for(i=0;i<least_squares.dimension;i++){
          if(gsl_vector_get(S,i) < trunc){
            j++;
            gsl_vector_set(S,i,0.0);
          }
        }
      }
      //Second truncation method.
      //Replace any singular values less than "trunc" with zero.
      //Trunc is calculated by the "median deviation" scheme.
      if(1==2){
        j=0;
        //Because the S vector is already sorted, the median is just
        //in the middle of the vector.
        if(least_squares.dimension % 2 == 0) median = 0.5*(gsl_vector_get(S,least_squares.dimension/2)+gsl_vector_get(S,least_squares.dimension/2-1));
        else median = gsl_vector_get(S,(least_squares.dimension-1)/2);
        //Now calculate md, the median deviation.
        md = 0;
        for(i=0;i<least_squares.dimension;i++){
          md += fabs(gsl_vector_get(S,i)-median);
        }
        md = md/least_squares.dimension;
        //"trunc" is just median - (#)*(md)
        trunc = 0.5*median;// - 3.0*md;
        if(trunc < 0.0) trunc = 0.0;//Just making sure...
        cout << "Maximum singular value = "<<gsl_vector_max(S)<<endl;
        cout << "Minimum singular value = "<<gsl_vector_min(S)<<endl;
        cout << "Median singular value  = "<<median<<endl;
        cout << "Median deviation       = "<<md<<endl;

        for(i=0;i<least_squares.dimension;i++){
          if(gsl_vector_get(S,i) <= trunc){
            j++;
            gsl_vector_set(S,i,0.0);
          }
        }
      }

      //printf ("S = \n");
      //gsl_vector_fprintf (stdout, S, "%g");

      cout<<results.options.prefix<<": truncated "<<j<<" (out of "<<least_squares.dimension<<") singular values smaller than "<<trunc<<"."<<endl;

      //Now solve using only the non-zero singular values.
      gsl_linalg_SV_solve(least_squares.A, V, S, least_squares.b, x);

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Debug: print m2 amplitudes (usually 3rd paramter) for all grid pts.
      //for(i=0;i<(long long)grid.lat.size();i++) cout<<"grid pt #"<<i+1<<" has 3rd parameter = "<<results.outputs.at(2).at(i)<<endl;

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares.A);
      gsl_vector_free(least_squares.b);
      gsl_vector_free(x);
      gsl_vector_free(S);
      gsl_matrix_free(V);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 112:
    case 113:{//Invert input data to pt. masses located at grid centers,
              //using truncated, square SVD using fast, not-accurate fctn.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 112) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 1;
        return;
      }
      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //Matrices for Ax=b are created using results.options.h_matrix_choice.
      least_squares = create_least_squares_in_parallel(input, grid, results);

      //Solve Ax=b using SV decomposition.
      gsl_vector * x = gsl_vector_alloc(least_squares.dimension);
      gsl_vector * S = gsl_vector_alloc(least_squares.dimension);
      gsl_matrix * V = gsl_matrix_alloc(least_squares.dimension,least_squares.dimension);
gsl_vector * temp_vector = gsl_vector_alloc(least_squares.dimension);
      gsl_matrix * temp_matrix = gsl_matrix_alloc(least_squares.dimension,least_squares.dimension);
      gsl_vector_set_zero(x);
      gsl_vector_set_zero(S);
      gsl_matrix_set_zero(V);
      gsl_vector_set_zero(temp_vector);
      gsl_matrix_set_zero(temp_matrix);

      cout<<results.options.prefix<<": Solving Ax=b using gsl_linalg_SV_decomp_mod() and gsl_linalg_SV_solve()."<<endl;
      gsl_linalg_SV_decomp_mod (least_squares.A, temp_matrix, V, S, temp_vector);

      //First truncation method.
      //Replace any singular values less than "trunc" with zero.
      //Trunc is calculated by dividing the maximum singular value by...
      double trunc = gsl_vector_max(S)/50.0;
      if(1==1){
        if(results.options.region_index == -1){
          cout << "Maximum singular value = "<<gsl_vector_max(S)<<endl;
          cout << "Minimum singular value = "<<gsl_vector_min(S)<<endl;
        }
        else{
          cout<<results.options.prefix<<": maximum singular value = "<<gsl_vector_max(S)<<endl;
          cout<<results.options.prefix<<": minimum singular value = "<<gsl_vector_min(S)<<endl;
        }
        j=0;
        for(i=0;i<least_squares.dimension;i++){
          if(gsl_vector_get(S,i) <= trunc){
            j++;
            gsl_vector_set(S,i,0.0);
          }
        }
      }

      //Now solve using only the non-zero singular values.
      gsl_linalg_SV_solve(least_squares.A, V, S, least_squares.b, x);

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares.A);
      gsl_vector_free(least_squares.b);
      gsl_vector_free(x);
      gsl_vector_free(S);
      gsl_matrix_free(V);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 114:
    case 115:{//Invert input data to pt. masses located at grid centers,
              //using sliced, square SVD using slow, accurate function.
              //Sliced means that only a single SVD is non-zero, it is
              //set in the code below.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 114) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 1;
        return;
      }
      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //Matrices for Ax=b are created using results.options.h_matrix_choice.
      least_squares = create_least_squares_in_parallel(input, grid, results);

      //Solve Ax=b using SV decomposition.
      gsl_vector * x = gsl_vector_alloc(least_squares.dimension);
      gsl_vector * S = gsl_vector_alloc(least_squares.dimension);
      gsl_matrix * V = gsl_matrix_alloc(least_squares.dimension,least_squares.dimension);
      gsl_vector_set_zero(x);
      gsl_vector_set_zero(S);
      gsl_matrix_set_zero(V);

      cout<<results.options.prefix<<": Solving Ax=b using gsl_linalg_SV_decomp_jacobi() and gsl_linalg_SV_solve()."<<endl;

      gsl_linalg_SV_decomp_jacobi(least_squares.A, V, S);

      //Slice SVD matrix - zero sing values at all indices except no_slice.
      long long no_slice=0;
      double slice_value = gsl_vector_get(S,no_slice);
      if(1==1){
        j=0;
        for(i=0;i<least_squares.dimension;i++){
          if(i != no_slice){
            j++;
            gsl_vector_set(S,i,0.0);

          }
        }
      }

      //printf ("S = \n");
      //gsl_vector_fprintf (stdout, S, "%g");

      cout<<results.options.prefix<<": Sliced "<<j<<" (out of "<<least_squares.dimension<<") singular values that were not in index "<<no_slice<<" (value "<<slice_value<<")"<<endl;

      //Now solve using only the non-zero singular values.
      gsl_linalg_SV_solve(least_squares.A, V, S, least_squares.b, x);

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares.A);
      gsl_vector_free(least_squares.b);
      gsl_vector_free(x);
      gsl_vector_free(S);
      gsl_matrix_free(V);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 116:
    case 117:{//Invert input data via LU decomposition after adding
              //gaussian smoothing term (weighted w hard coded value) to
              //the penalty function.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 116) results.options.sincos = 1;
        else results.options.sincos = 0;
        //Create a gaussian smoothing term to add to least_squares.A.
        results.options.regularization_choice = 0;//gaussian.
        results.options.uses_least_squares = 1;
        return;
      }
      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //Matrices for Ax=b are created using results.options.h_matrix_choice.
      least_squares = create_least_squares_in_parallel(input, grid, results);

      //Gaussian halfwidth is set in the options file.
      weight = results.options.lambda;

      cout<<results.options.prefix<<": Creating regularization term using Gaussian smoothing of radius "<<results.options.regparam1<<" km."<<endl;
      temp_least_squares = regularize(grid, results);
      gsl_matrix_scale (temp_least_squares.R,weight);
      gsl_matrix_add (least_squares.A,temp_least_squares.R);

      //Solve Ax=b using these solution routines:
      string solution_routines("gsl_linalg_LU_decomp() and gsl_linalg_LU_solve()");
      cout<<results.options.prefix<<": Solving Ax=b using "<<solution_routines<<", using "<<      temp_least_squares.reg_title<<" regularization of weight "<<weight<<" and radius "<<results.options.regparam1<<" km."<<endl;

      int gsl_s;
      gsl_permutation * p = gsl_permutation_alloc(least_squares.dimension);
      gsl_vector * x = gsl_vector_alloc(least_squares.dimension);
      gsl_linalg_LU_decomp(least_squares.A, p, &gsl_s);
      gsl_linalg_LU_solve (least_squares.A, p, least_squares.b, x);

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares.A);
      gsl_vector_free(least_squares.b);
      gsl_vector_free(x);
      gsl_permutation_free(p);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 118:
    case 119:{//Invert input data via LU decomposition after adding
              //Tikhonov regular. term (weighted w hard coded value) to
              //the penalty function.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 118) results.options.sincos = 1;
        else results.options.sincos = 0;
        //Create a regularization term to add to least_squares.A.
        results.options.regularization_choice = 1;//Tikhonov
        results.options.uses_least_squares = 1;
        return;
      }
      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints and 1==2){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //Matrices for Ax=b are created using results.options.h_matrix_choice.
      least_squares = create_least_squares_in_parallel(input, grid, results);

      //DEBUG
      if(1==2){
        long long visible_elements=20;
        if(visible_elements > least_squares.dimension)
          visible_elements = least_squares.dimension;

        for(i=0;i<visible_elements;i++){
          cout<<"b["<<i<<"] = "<<gsl_vector_get(least_squares.b, i)<<endl;
        }
        cout<<"*************************************************"<<endl;
        cout<<"*************************************************"<<endl;
        for(i=0;i<visible_elements;i++){
          for(k=0;k<visible_elements;k++){
            cout<<"A["<<i<<"]["<<k<<"] = "<<gsl_matrix_get(least_squares.A, i, k)<<endl;
          }
        }
        cout<<"*************************************************"<<endl;
        cout<<"*************************************************"<<endl;
      }

      temp_least_squares = regularize(grid, results);
      weight = results.options.lambda;
      gsl_matrix_scale (temp_least_squares.R,weight);
      gsl_matrix_add (least_squares.A,temp_least_squares.R);

      //Solve Ax=b using these solution routines:
      string solution_routines("gsl_linalg_LU_decomp() and gsl_linalg_LU_solve()");
      cout<<results.options.prefix<<": Solving Ax=b using "<<solution_routines<<", using "<<      temp_least_squares.reg_title<<" regularization of weight "<<weight<<endl;

      int gsl_s;
      gsl_permutation * p = gsl_permutation_alloc(least_squares.dimension);
      gsl_vector * x = gsl_vector_alloc(least_squares.dimension);

      time_t pre_solve_time = time(NULL);

      gsl_linalg_LU_decomp(least_squares.A, p, &gsl_s);
      gsl_linalg_LU_solve (least_squares.A, p, least_squares.b, x);

      time_t time_solved = time(NULL);
      timespan_conversion_s timespan_conversion;
      timespan_conversion.seconds=(long long)(time_solved-pre_solve_time);
      cout<<results.options.prefix<<" "<<solution_routines<<" solution routines took"<<sec2human(timespan_conversion)<<endl;

      //DEBUG
      if(1==2){
        long long visible_elements=20;
        if(visible_elements > least_squares.dimension)
          visible_elements = least_squares.dimension;

        for(i=0;i<visible_elements;i++){
          cout<<"x["<<i<<"] = "<<gsl_vector_get(x, i)<<endl;
        }
      }

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares.A);
      gsl_vector_free(least_squares.b);
      gsl_vector_free(x);
      gsl_permutation_free(p);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 120:
    case 121:{//Invert input data via REFINED LU decomposition after adding
              //Tikhonov regularizatn term (weighted w hard coded value) to
              //the penalty function.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.

      if(results.options.inside_load_options==1){
        if(results.options.type == 120) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.regularization_choice = 1;//Tikhonov
        results.options.uses_least_squares = 1;
        return;
      }
      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //Matrices for Ax=b are created using results.options.h_matrix_choice.
      least_squares = create_least_squares_in_parallel(input, grid, results);

      //Create a regularization term to add to least_squares.A.

      //cout<<results.options.prefix<<": Creating Tikhonov regularization term."<<endl;
      temp_least_squares = regularize(grid, results);
      weight = results.options.lambda;
      gsl_matrix_scale (temp_least_squares.R,weight);
      gsl_matrix_add (least_squares.A,temp_least_squares.R);

      cout<<results.options.prefix<<": Solving Ax=b using gsl_linalg_LU_decomp() and gsl_linalg_LU_solve(), using Tikhonov regularization of weight "<<weight<<endl;

      int gsl_s;
      gsl_permutation * p = gsl_permutation_alloc(least_squares.dimension);
      gsl_vector * x = gsl_vector_alloc(least_squares.dimension);

      //Need for refinement because LU_solve replaces A with LU.
      gsl_matrix * A_copy = gsl_matrix_alloc(least_squares.dimension,least_squares.dimension);
      gsl_matrix_memcpy(A_copy, least_squares.A);

      //Back to the solvin...
      gsl_linalg_LU_decomp(least_squares.A, p, &gsl_s);
      gsl_linalg_LU_solve (least_squares.A, p, least_squares.b, x);

      //Need for refinement because LU_refine... refines... x.
      gsl_vector * x_copy = gsl_vector_alloc(least_squares.dimension);
      gsl_vector_memcpy(x_copy, x);

      //Refine the solution until its misfit- which is the relative rms
      //difference between one solution and the next- is < convergence
      gsl_vector * residual = gsl_vector_alloc(least_squares.dimension);

      double misfit,norm_x,norm_delta_x;
      long long finished_refining=0;
      long long num_refines=0,min_refines=2,max_refines=10;
      while(finished_refining == 0){
        num_refines++;
        gsl_linalg_LU_refine (A_copy, least_squares.A, p, least_squares.b, x, residual);
        //Calc misfit
        norm_x=0; norm_delta_x=0;
        for(i=0;i<least_squares.dimension;i++){
          //norm of old
          norm_x += pow(gsl_vector_get(x_copy,i),2);

          //norm of delta x.
          norm_delta_x += pow(gsl_vector_get(x,i)-gsl_vector_get(x_copy,i),2);
        }
        norm_x=sqrt(norm_x);
        norm_delta_x=sqrt(norm_delta_x);
        misfit=norm_delta_x/norm_x;
        //copy new x vector to x_copy vector for next time.
        gsl_vector_memcpy(x_copy, x);

        cout<<"Misfit #"<<num_refines<<": "<<scientific<<setprecision(12)<<misfit<<" (convergence: "<<scientific<<setprecision(12)<<results.options.convergence<<")."<<endl;

        if(misfit < results.options.convergence and num_refines >= min_refines) finished_refining=1;
        if(num_refines >= max_refines) finished_refining=1;
      }//End of refinements loop.

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares.A);
      gsl_matrix_free(A_copy);
      gsl_vector_free(least_squares.b);
      gsl_vector_free(x);
      gsl_vector_free(residual);
      gsl_permutation_free(p);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 122:
    case 123://Invert input data via LU decomposition after adding
             //Tikhonov regularizatn term (weighted w hard coded value) to
             //the penalty function. Done in-place so less memory used.
             //1st returns cos,sin coef; 2nd returns quadr. amp and phase.
    case 124:
    case 125:{//Invert input data via Householder transformations after adding
              //Tikhonov regularizatn term (weighted w hard coded value) to
              //the penalty function. Done in-place so less memory used.
              //1st returns cos,sin coef; 2nd returns quadr. amp and phase.
      
      if(results.options.inside_load_options==1){
        if(results.options.type == 122 or results.options.type == 124) results.options.sincos = 1;
        else results.options.sincos = 0;
        //Regularize least_squares.A.
        results.options.regularization_choice = 1;//Tikhonov
        results.options.uses_least_squares = 1;
        return;
      }
      //Don't run analysis unless input.time.size() <= minimum_inputpoints.
      //Check that minimum_inputpoints is enough to insure that the
      //system is over-determined.
      if(minimum_inputpoints < (long long)(grid.lat.size()*results.options.output_type.size())) minimum_inputpoints = grid.lat.size()*results.options.output_type.size();
      if((long long)input.time.size() <= minimum_inputpoints and 1==2){
        cout<<"input.time.size()="<<input.time.size()<<" while grid.lat.size()*results.options.output_type.size()="<<grid.lat.size()*results.options.output_type.size()<<endl;
        cout<<"The minimum number of inputpoints is "<<minimum_inputpoints<<endl;
        cout<<"As a result, analysis in this region is being skipped."<<endl;
        return;
      }

      //Matrices for Ax=b are created using results.options.h_matrix_choice.
      least_squares = create_least_squares_in_parallel(input, grid, results);

      regularize_in_place(grid, results, least_squares);

      //Solve Ax=b using these solution routines:
      string solution_routines;
      if(results.options.type == 122 or results.options.type == 123){
        solution_routines = "gsl_linalg_LU_decomp() and gsl_linalg_LU_solve()";
      }
      else{
        solution_routines = "gsl_linalg_HH_solve()";
      }

      cout<<results.options.prefix<<": Solving Ax=b with "<<solution_routines<<", using "<<least_squares.reg_title<<" regularization of weight "<<results.options.lambda<<endl;

      int gsl_s;
      gsl_permutation * p = gsl_permutation_alloc(least_squares.dimension);
      gsl_vector * x = gsl_vector_alloc(least_squares.dimension);

      time_t pre_solve_time = time(NULL);

      if(results.options.type == 122 or results.options.type == 123){
        gsl_linalg_LU_decomp(least_squares.A, p, &gsl_s);
        gsl_linalg_LU_solve (least_squares.A, p, least_squares.b, x);
      }
      else{
        gsl_linalg_HH_solve(least_squares.A, least_squares.b, x);
      }

      time_t time_solved = time(NULL);
      timespan_conversion_s timespan_conversion;
      timespan_conversion.seconds=(long long)(time_solved-pre_solve_time);
      cout<<results.options.prefix<<" "<<solution_routines<<" solution routines took"<<sec2human(timespan_conversion)<<endl;

      //Copy the results to outputs depending on analysis type.
      copy_x_to_outputs(results,grid,x);

      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares.A);
      gsl_vector_free(least_squares.b);
      gsl_vector_free(x);
      gsl_permutation_free(p);

      break;
    }
    #else 
      cout<<"These types require GSL. Search definitions.cpp for GSL_HERE."<<endl;
    #endif
    //////////////////////////////////////////////////////////////////////
    /////////////////////////////MODEL ONLY///////////////////////////////
    //////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1001:{ //Display ALL model amplitudes at specified grid points.

      if(results.options.inside_load_options==1){
        //Define model periods so they can over-write temp_results.
        model_s temp_model;
        temp_model.options.type=2;//FES, using modern double pass binning.
        define_model_titles_and_periods(temp_model);
        results.options.parameter = temp_model.periods;
        results.options.parameter_type.clear();
        results.options.parameter_type.resize(results.options.parameter.size(),101);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      //Loop through model periods ("M2", "K1", etc)
      for(i=0;i<(long long)model.titles.size();i++){
        //Set title.
        results.titles.push_back(model.titles.at(i));
        results.titles.at(i).append(" Amplitude");
        results.units.push_back("cm");
        //Loop through grid points.
        for(j=0;j<(long long)grid.lat.size();j++){
          results.outputs.at(i).at(j) = model.amplitudes.at(i).at(j);
        }//End of loop through grid points.
      }//End of loop through periods.

      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1002:{ //Display ALL model phases at specified grid points.

      if(results.options.inside_load_options==1){
        //Define model periods so they can over-write temp_results.
        model_s temp_model;
        temp_model.options.type=2;//FES, using modern double pass binning.
        define_model_titles_and_periods(temp_model);
        results.options.parameter = temp_model.periods;
        results.options.parameter_type.clear();
        results.options.parameter_type.resize(results.options.parameter.size(),101);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      //Loop through model periods ("M2", "K1", etc)
      for(i=0;i<(long long)model.titles.size();i++){
        //Set title.
        results.titles.push_back(model.titles.at(i));
        results.titles.at(i).append(" Phase");
        results.units.push_back("degrees");
        //Loop through grid points.
        for(j=0;j<(long long)grid.lat.size();j++){
          results.outputs.at(i).at(j) = model.phases.at(i).at(j);
        }//End of loop through grid points.
      }//End of loop through periods

      //Unless changed, all types convert from input.epoch to J2000.
      //type 1002 is an exception, where phases aren't disturbed.
      results.options.adjust_phases_choice = -1;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1003:{ //Create loadable model output.

      if(results.options.inside_load_options==1){
        //Define model periods so they can over-write temp_results.
        model_s temp_model;
        temp_model.options.type=2;//FES, using modern double pass binning.
        define_model_titles_and_periods(temp_model);
        results.options.parameter = temp_model.periods;
        results.options.parameter_type.clear();
        results.options.parameter_type.resize(results.options.parameter.size(),101);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      convert_model_to_results(grid,results,model);
      //Unless changed, all types convert from input.epoch to J2000.
      //MasconFES - type 1003 - is an exception.
      results.options.adjust_phases_choice = 2;
      results.options.current_phase_format = 2;

      break;
    }

    //////////////////////////////////////////////////////////////////////
    ///////////////////////////MODEL AND INPUT////////////////////////////
    //////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////
    //////////////////////////SYNTHETIC OUTPUT////////////////////////////
    //////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10001:
    case 10002:{
      //Parameters - const, lin, m2, annual.  Amps on order of 1cm.
      //Const. - positive from 55-60, 65-70, 75-80,85-90, else negative
      //Trend  - negative from ", positive otherwise. - only on land!
      //M2     - lat-dependent amplitude (highest at pole), phase = lon-dep
      //         (constant in each quadrant) - only in ocean!
      //Annual - const amp in ocean only- phase varies smoothly with lon.
      typesincos = 10001;

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(annual_period);//Annual
        results.options.parameter_type.push_back(101);

        if(results.options.type == typesincos) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.
      
      //Constant.
      k++; temp_double = 3.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) - 10.0*floor(grid.lat.at(i)/10.0) > 5.0 or grid.lat.at(i) > 89.9){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //Trend.
      k++; temp_double = 2.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0){//Only on land!
          if(grid.lat.at(i) - 10*floor(grid.lat.at(i)/10.0) > 5.0){
            results.outputs.at(k).at(i) = -temp_double;
          }
          else results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Amplitude.
      k++; temp_double = 5.0;
      cout<<"M2 amp at pole is "<<temp_double<<" cm, decreasing to 0 at 55N."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean above 55N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) >= 55.0){
          results.outputs.at(k).at(i) = temp_double*((grid.lat.at(i)-55.0)/(90.0-55.0));
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; temp_double = 60.0;
      cout<<"M2 phase is +/- "<<temp_double<<" deg ('+' for lon 0..90,180..270, '-' for lon 90..180,270..360)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lon.at(i) < 90.0 and grid.lon.at(i) >= 0.0) or (grid.lon.at(i) < 270.0 and grid.lon.at(i) >= 180.0)){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //Annual Amplitude.
      k++; temp_double = 1.0;
      cout<<"Annual amp is "<<temp_double<<" cm everywhere in ocean."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //Annual Phase.
      k++;
      cout<<"Annual phase is equal to longitude."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = grid.lon.at(i);
      }

      //If necessary, convert to sincos
      if(results.options.type == typesincos){
        convert_results_from_ampphase_to_sincos(results);
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10003:
    case 10004:{
      //Parameters - const, lin, m2, annual. Amps on order of 100cm.
      //Const. - positive from 50-70, negative otherwise
      //Trend - negative from 55-60, 65-70, 75-80,85-90, positive
      //         otherwise. - only on land!
      //M2     - lat-dependent amplitude (highest at pole), phase = lon-dep
      //         (constant in each quadrant) - only in ocean!
      //Annual - const amp in ocean only- phase varies smoothly with lon.
      typesincos = 10003;

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(annual_period);//Annual
        results.options.parameter_type.push_back(101);

        if(results.options.type == typesincos) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.
      
      //Constant.
      k++; temp_double = 300.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) > 50.0 and grid.lat.at(i)< 70.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //Trend.
      k++; temp_double = 200.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0){//Only on land!
          if(grid.lat.at(i) - 10*floor(grid.lat.at(i)/10.0) > 5.0){
            results.outputs.at(k).at(i) = -temp_double;
          }
          else results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Amplitude.
      k++; temp_double = 500.0;
      cout<<"M2 amp at pole is "<<temp_double<<" cm, decreasing to 0 at 55N."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean above 55N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) >= 55.0){
          results.outputs.at(k).at(i) = temp_double*((grid.lat.at(i)-55.0)/(90.0-55.0));
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; temp_double = 60.0;
      cout<<"M2 phase is +/- "<<temp_double<<" deg ('+' for lon 0..90,180..270, '-' for lon 90..180,270..360)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lon.at(i) < 90.0 and grid.lon.at(i) >= 0.0) or (grid.lon.at(i) < 270.0 and grid.lon.at(i) >= 180.0)){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //Annual Amplitude.
      k++; temp_double = 100.0;
      cout<<"Annual amp is "<<temp_double<<" cm everywhere in ocean."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //Annual Phase.
      k++;
      cout<<"Annual phase is equal to longitude."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = grid.lon.at(i);
      }

      //If necessary, convert to sincos
      if(results.options.type == typesincos){
        convert_results_from_ampphase_to_sincos(results);
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10005:
    case 10006:
    case 10017:{
      //Parameters - const, lin, m2, annual. Amps on order of 10cm.
      //Const. - positive south of 75N, negative otherwise
      //Trend - lon-dep: negative from 0-60, 120-180, 240-300, positive
      //         otherwise. - only on land!
      //M2     - lat-dependent amplitude (highest at pole), phase = lon-dep
      //         (constant in each quadrant) - only in ocean!
      //Annual - const amp in ocean only- phase varies smoothly with lon.
      //Type 10017 is the same but with annual replaced with k1.
      typesincos = 10005;
      long long late_addition=10017;

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        if(results.options.type == late_addition){
          results.options.parameter.push_back(k1_period);//K1
          results.options.parameter_type.push_back(101);
        }
        else{
          results.options.parameter.push_back(annual_period);//Annual
          results.options.parameter_type.push_back(101);
        }

        if(results.options.type == typesincos) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.
      
      //Constant.
      k++; temp_double = 3.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i)< 75.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //Trend.
      k++; temp_double = 2.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0){//Only on land!
          if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0){
            results.outputs.at(k).at(i) = temp_double;
          }
          else results.outputs.at(k).at(i) = -temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Amplitude.
      k++; temp_double = 12.0;
      cout<<"M2 amp at pole is "<<temp_double<<" cm, decreasing to 0 at 55N."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean above 55N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) >= 55.0){
          results.outputs.at(k).at(i) = temp_double*((grid.lat.at(i)-55.0)/(90.0-55.0));
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; temp_double = 60.0;
      cout<<"M2 phase is +/- "<<temp_double<<" deg ('+' for lon 0..90,180..270, '-' for lon 90..180,270..360)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lon.at(i) < 90.0 and grid.lon.at(i) >= 0.0) or (grid.lon.at(i) < 270.0 and grid.lon.at(i) >= 180.0)){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //K1/Annual Amplitude.
      k++; temp_double = 10.0;
      if(results.options.type == late_addition) cout<<"K1 amp is "<<temp_double<<" cm everywhere in ocean."<<endl;
      else cout<<"Annual amp is "<<temp_double<<" cm everywhere in ocean."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //K1/Annual Phase.
      k++;
      if(results.options.type == late_addition) cout<<"K1 phase is equal to longitude."<<endl;
      else cout<<"Annual phase is equal to longitude."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = grid.lon.at(i);
      }

      //If necessary, convert to sincos
      if(results.options.type == typesincos){
        convert_results_from_ampphase_to_sincos(results);
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10007:
    case 10008:{
      //Parameters - const, lin, m2, annual. Amps on order of 10cm.
      //Const. - single pt at 60N, 0E.
      //Trend - single pt at 60N, 90E.
      //M2     - single pt at 60N, 180E.
      //Annual - single pt at 60N, 270E.
      typesincos = 10007;

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(annual_period);//Annual
        results.options.parameter_type.push_back(101);

        if(results.options.type == typesincos) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.
      
      //Constant.
      k++; temp_double = 3.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 0.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //Trend.
      k++; temp_double = 2.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 90.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //M2 Amplitude.
      k++; temp_double = 12.0;
      cout<<"M2 amp is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 180.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //M2 Phase.
      k++; temp_double = 60.0;
      cout<<"M2 phase is "<<temp_double<<" deg."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 180.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //Annual Amplitude.
      k++; temp_double = 10.0;
      cout<<"Annual amp is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 270.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //Annual Phase.
      k++; temp_double = 60.0;
      cout<<"Annual phase is "<<temp_double<<" deg."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 270.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //If necessary, convert to sincos
      if(results.options.type == typesincos){
        convert_results_from_ampphase_to_sincos(results);
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10009:
    case 10010:{
      //Parameters - const, lin, m2, annual. Amps on order of 10cm.
      //All at single pt at 62N, 90E.
      typesincos = 10009;

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(annual_period);//Annual
        results.options.parameter_type.push_back(101);

        if(results.options.type == typesincos) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.
      
      double desired_lat = 62.0, desired_lon = 90;

      //Constant.
      k++; temp_double = 3.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      cout<<"Closest matching grid point has lat "<<grid.lat.at(index)<<" and lon "<<grid.lon.at(index)<<endl;
      results.outputs.at(k).at(index) = temp_double;

      //Trend.
      k++; temp_double = 2.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //M2 Amplitude.
      k++; temp_double = 12.0;
      cout<<"M2 amp is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //M2 Phase.
      k++; temp_double = 60.0;
      cout<<"M2 phase is "<<temp_double<<" deg."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //Annual Amplitude.
      k++; temp_double = 10.0;
      cout<<"Annual amp is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //Annual Phase.
      k++; temp_double = 60.0;
      cout<<"Annual phase is "<<temp_double<<" deg."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //If necessary, convert to sincos
      if(results.options.type == typesincos){
        convert_results_from_ampphase_to_sincos(results);
      }
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10011:
    case 10012:
    case 10014:{
      //10011,12: Params - const, lin, m2, annual. Amps on order of 10cm.
      //10014: Params - const, lin, m2, k1. Amps on order of 10cm.
      //All at single pt at NP
      //late_addition means that it was added later and grafted onto
      //existing code to avoid code duplication.
      typesincos = 10011;
      long long late_addition=10014;

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        if(results.options.type == late_addition){
          results.options.parameter.push_back(k1_period);//K1
          results.options.parameter_type.push_back(101);
        }
        if(results.options.type == late_addition){
          results.options.parameter.push_back(annual_period);//Annual
          results.options.parameter_type.push_back(101);
        }

        if(results.options.type == typesincos) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.
      
      double desired_lat=90.0,desired_lon=0.0;

      //Constant.
      k++; temp_double = 3.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      cout<<"Closest matching grid point has lat "<<grid.lat.at(index)<<" and lon "<<grid.lon.at(index)<<endl;
      results.outputs.at(k).at(index) = temp_double;

      //Trend.
      k++; temp_double = 2.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //M2 Amplitude.
      k++; temp_double = 12.0;
      cout<<"M2 amp is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //M2 Phase.
      k++; temp_double = 60.0;
      cout<<"M2 phase is "<<temp_double<<" deg."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //Annual/K1 Amplitude.
      k++; temp_double = 10.0;
      if(results.options.type == late_addition) cout<<"K1 amp is "<<temp_double<<" cm."<<endl;
      else cout<<"Annual amp is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //Annual/K1 Phase.
      k++; temp_double = 60.0;
      if(results.options.type == late_addition) cout<<"K1 phase is "<<temp_double<<" deg."<<endl;
      else cout<<"Annual phase is "<<temp_double<<" deg."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //If necessary, convert to sincos
      if(results.options.type == typesincos){
        convert_results_from_ampphase_to_sincos(results);
      }
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10013:{
      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.
      
      //Constant.
      k++; temp_double = 10.0;
      cout<<"Constant is "<<temp_double<<" cm everywhere in ocean."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10015:
    case 10016:{
      //Parameters - const, lin, m2, k1. Amps on order of 10cm.
      //All at single pt at 62N, 35E
      typesincos = 10015;

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;//INITIALLY in amp/phase format!
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      double desired_lat=62.0,desired_lon=35.0;

      //Constant.
      k++; temp_double = 10.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      cout<<"Closest matching grid point has lat "<<grid.lat.at(index)<<" and lon "<<grid.lon.at(index)<<endl;
      results.outputs.at(k).at(index) = temp_double;

      //Trend.
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //M2 Amplitude.
      k++; temp_double = 10.0;
      cout<<"M2 amp is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //M2 Phase.
      k++; temp_double = 0.0;
      cout<<"M2 phase is "<<temp_double<<" deg."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //K1 Amplitude.
      k++; temp_double = 10.0;
      cout<<"K1 amp is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //K1 Phase.
      k++; temp_double = 0.0;
      cout<<"K1 phase is "<<temp_double<<" deg."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //If necessary, convert to sincos
      if(results.options.type == typesincos){
        convert_results_from_ampphase_to_sincos(results);
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10018:{
      //Parameters - const, lin, m2, k1. Amps on order of 10cm.
      //Const. - positive south of 75N, negative otherwise
      //Trend - 10 cm/yr in ocean only.
      //M2     - lat-dependent amplitude (highest at pole), phase = lon-dep
      //         (constant in each quadrant) - only in ocean!
      //K1     - 2cm amp on land only w lon-dep phase: -90 from 0-60,
      //         120-180, 240-300, +90 otherwise.
      //This case is same as 10017 but with k1 and trend patterns swapped.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.
      
      //Constant.
      k++; temp_double = 3.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i)< 75.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //Trend.
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0)//Only in ocean!
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Amplitude.
      k++; temp_double = 12.0;
      cout<<"M2 amp at pole is "<<temp_double<<" cm, decreasing to 0 at 55N."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean above 55N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) >= 55.0){
          results.outputs.at(k).at(i) = temp_double*((grid.lat.at(i)-55.0)/(90.0-55.0));
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; temp_double = 60.0;
      cout<<"M2 phase is +/- "<<temp_double<<" deg ('+' for lon 0..90,180..270, '-' for lon 90..180,270..360)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lon.at(i) < 90.0 and grid.lon.at(i) >= 0.0) or (grid.lon.at(i) < 270.0 and grid.lon.at(i) >= 180.0)){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //K1 Amplitude.
      k++; temp_double = 2.0;
      cout<<"K1 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is +/- "<<temp_double<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10019:{
      //Copy of 10018, but the constant is uniformly positive everywhere.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant.
      k++; temp_double = 3.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++)
        results.outputs.at(k).at(i) = temp_double;

      //Trend.
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0)//Only in ocean!
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Amplitude.
      k++; temp_double = 12.0;
      cout<<"M2 amp at pole is "<<temp_double<<" cm, decreasing to 0 at 55N."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean above 55N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) >= 55.0){
          results.outputs.at(k).at(i) = temp_double*((grid.lat.at(i)-55.0)/(90.0-55.0));
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; temp_double = 60.0;
      cout<<"M2 phase is +/- "<<temp_double<<" deg ('+' for lon 0..90,180..270, '-' for lon 90..180,270..360)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lon.at(i) < 90.0 and grid.lon.at(i) >= 0.0) or (grid.lon.at(i) < 270.0 and grid.lon.at(i) >= 180.0)){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //K1 Amplitude.
      k++; temp_double = 2.0;
      cout<<"K1 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is +/- "<<temp_double<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10020:{ //Constant is uniformly positive everywhere.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant.
      k++; temp_double = 10.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++)
        results.outputs.at(k).at(i) = temp_double;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10021:{
      //10021- Copy of trend from 10017,10006.  2cm amp.
      //Trend - neg from lon=0-60, 120-180, 240-300, else pos. land only!

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Trend.
      k++; temp_double = 2.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0){//Only on land!
          if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0){
            results.outputs.at(k).at(i) = temp_double;
          }
          else results.outputs.at(k).at(i) = -temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10022:{
      //Copy of 10018, but const looks like a bullseye.
      //Parameters - const, lin, m2, k1. Amps on order of 10cm.
      //Const. - positive between 70N and 80N, negative otherwise
      //Trend - uniform trend in ocean only.
      //M2     - lat-dependent amplitude (highest at pole), phase = lon-dep
      //         (constant in each quadrant) - only in ocean!
      //K1     - uniform amp on land only w lon-dep phase: -90 from 0-60,
      //         120-180, 240-300, +90 otherwise.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant.
      k++; temp_double = 3.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) < 80.0 and grid.lat.at(i) > 70.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //Trend.
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0)//Only in ocean!
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Amplitude.
      k++; temp_double = 12.0;
      cout<<"M2 amp at pole is "<<temp_double<<" cm, decreasing to 0 at 55N."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean above 55N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) >= 55.0){
          results.outputs.at(k).at(i) = temp_double*((grid.lat.at(i)-55.0)/(90.0-55.0));
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; temp_double = 60.0;
      cout<<"M2 phase is +/- "<<temp_double<<" deg ('+' for lon 0..90,180..270, '-' for lon 90..180,270..360)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lon.at(i) < 90.0 and grid.lon.at(i) >= 0.0) or (grid.lon.at(i) < 270.0 and grid.lon.at(i) >= 180.0)){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //K1 Amplitude.
      k++; temp_double = 2.0;
      cout<<"K1 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is +/- "<<temp_double<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10023:{
      //10023- Copy of 10022, but no trend.
      //Parameters - const, m2, k1. Amps on order of 10cm.
      //Const. - positive between 70N and 80N, negative otherwise
      //M2     - lat-dependent amplitude (highest at pole), phase = lon-dep
      //         (constant in each quadrant) - only in ocean!
      //K1     - uniform amp on land only w lon-dep phase: -90 from 0-60,
      //         120-180, 240-300, +90 otherwise.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant.
      k++; temp_double = 3.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) < 80.0 and grid.lat.at(i) > 70.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //M2 Amplitude.
      k++; temp_double = 12.0;
      cout<<"M2 amp at pole is "<<temp_double<<" cm, decreasing to 0 at 55N."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean above 55N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) >= 55.0){
          results.outputs.at(k).at(i) = temp_double*((grid.lat.at(i)-55.0)/(90.0-55.0));
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; temp_double = 60.0;
      cout<<"M2 phase is +/- "<<temp_double<<" deg ('+' for lon 0..90,180..270, '-' for lon 90..180,270..360)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lon.at(i) < 90.0 and grid.lon.at(i) >= 0.0) or (grid.lon.at(i) < 270.0 and grid.lon.at(i) >= 180.0)){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //K1 Amplitude.
      k++; temp_double = 2.0;
      cout<<"K1 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is +/- "<<temp_double<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10024:{
      //10024- Copy of 10022, but M2 and K1 are swapped.
      //Parameters - const, lin, m2, k1. Amps on order of 10cm.
      //Const. - positive between 70N and 80N, negative otherwise
      //Trend - uniform trend in ocean only.
      //K1     - lat-dependent amplitude (highest at pole), phase = lon-dep
      //         (constant in each quadrant) - only in ocean!
      //M2     - uniform amp on land only w lon-dep phase: -90 from 0-60,
      //         120-180, 240-300, +90 otherwise.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant.
      k++; temp_double = 3.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) < 80.0 and grid.lat.at(i) > 70.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //Trend.
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0)//Only in ocean!
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Amplitude.
      k++; temp_double = 2.0;
      cout<<"M2 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 phase
      k++; temp_double = 90.0;
      cout<<"M2 phase is +/- "<<temp_double<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = -temp_double;
      }

      //K1 Amplitude.
      k++; temp_double = 12.0;
      cout<<"K1 amp at pole is "<<temp_double<<" cm, decreasing to 0 at 55N."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean above 55N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) >= 55.0){
          results.outputs.at(k).at(i) = temp_double*((grid.lat.at(i)-55.0)/(90.0-55.0));
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 Phase.
      k++; temp_double = 60.0;
      cout<<"K1 phase is +/- "<<temp_double<<" deg ('+' for lon 0..90,180..270, '-' for lon 90..180,270..360)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lon.at(i) < 90.0 and grid.lon.at(i) >= 0.0) or (grid.lon.at(i) < 270.0 and grid.lon.at(i) >= 180.0)){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10025:{
      //10025- Copy of 10023, but no M2 or const. K1 on land only.
      //Parameters - k1. Amplitude is 2cm.
      //K1     - uniform amp on land only w lon-dep phase: -90 from 0-60,
      //         120-180, 240-300, +90 otherwise.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //K1 Amplitude.
      k++; temp_double = 2.0;
      cout<<"K1 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is +/- "<<temp_double<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10026:{
      //Copy of 10022, but only trend and K1.
      //Parameters - lin, k1. Amps on order of 10cm.
      //Trend - uniform trend in ocean only.
      //K1     - uniform amp on land only w lon-dep phase: -90 from 0-60,
      //         120-180, 240-300, +90 otherwise.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Trend.
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0)//Only in ocean!
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 Amplitude.
      k++; temp_double = 2.0;
      cout<<"K1 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is +/- "<<temp_double<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10027:{
      //Copy of 10026, but only trend and K1 north of 65N.
      //Parameters - lin, k1. Amps on order of 10cm.
      //Trend - uniform trend in ocean north of 65N only.
      //K1     - uniform amp on land only w lon-dep phase: -90 from 0-60,
      //         120-180, 240-300, +90 otherwise. (only north of 65N)

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Trend.
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean north of 65N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) > 65.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 Amplitude.
      k++; temp_double = 2.0;
      cout<<"K1 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0 and grid.lat.at(i) > 65.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is +/- "<<temp_double<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10028:{
      //Copy of 10027, but trend replaced with const with same pattern.
      //Parameters - const, k1. Amps on order of 10cm.
      //Const  - uniform trend in ocean north of 65N only.
      //K1     - uniform amp on land only w lon-dep phase: -90 from 0-60,
      //         120-180, 240-300, +90 otherwise. (only north of 65N)

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant.
      k++; temp_double = 10.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean north of 65N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) > 65.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 Amplitude.
      k++; temp_double = 2.0;
      cout<<"K1 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0 and grid.lat.at(i) > 65.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is +/- "<<temp_double<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10029:{
      //Copy of 10027, but only trend north of 65N.
      //Parameters - lin. Amp = 10cm.
      //Trend - uniform trend in ocean north of 65N only.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Trend.
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean north of 65N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) > 65.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10030:{
      //Copy of 10029, but constant instead of trend.
      //Parameters - constant. Amp = 10cm.
      //Const - uniform trend in ocean north of 65N only.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant.
      k++; temp_double = 10.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean north of 65N!
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) > 65.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10031:{
      //Copy of 10029, but rotated by 90 degrees towards the west.
      //Parameters - lin. Amp = 10cm.
      //Trend - uniform trend in ocean north of 65N only.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Rotated trend.
      double temp_elev;
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Rotate the coastline by changing which elevation is used.
        temp_elev = nearest_topo_pt_elev(grid.lat.at(i),grid.lon.at(i)+90.0,ancillary);
        //Only in ocean north of 65N!
        if(temp_elev < 0.0 and grid.lat.at(i) > 65.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10032:{ //Trend - uniform trend in ocean.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Trend.
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean, as defined by water coverage!
        if(grid.wcover.at(i) > 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10033:{ //Constant - uniform amplitude in ocean.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant.
      k++; temp_double = 10.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean!
        if(grid.elev.at(i) < 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10034:{ //K1 Sine - uniform amplitude in ocean.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //K1 Amplitude.
      k++; temp_double = 10.0;
      cout<<"K1 amp is "<<temp_double<<" cm everywhere in ocean."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10035:{ //K2 Sine - uniform amplitude in ocean.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(k2_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //K2 Amplitude.
      k++; temp_double = 10.0;
      cout<<"K2 amp is "<<temp_double<<" cm everywhere in ocean."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K2 phase
      k++; temp_double = 90.0;
      cout<<"K2 phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10036:{
      //K1 Alias (From RL2006) Sine - uniform amplitude in ocean.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(k1_alias_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //K1 alias Amplitude.
      k++; temp_double = 10.0;
      cout<<"K1 alias amp is "<<temp_double<<" cm everywhere in ocean."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 alias phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10037:{
      //K2 Alias (From RL2006) Sine - uniform amplitude in ocean.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(k2_alias_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //K2 alias Amplitude.
      k++; temp_double = 10.0;
      cout<<"K2 alias amp is "<<temp_double<<" cm everywhere in ocean."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K2 phase
      k++; temp_double = 90.0;
      cout<<"K2 alias phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10038:{
      //K2 Alias (RL2006) Sine in ocean north of 65N, uniform amp of 10cm.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(k2_alias_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //K2 alias Amplitude.
      k++; temp_double = 10.0;
      cout<<"K2 alias amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) < 0.0 and grid.lat.at(i) > 65.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K2 phase
      k++; temp_double = 90.0;
      cout<<"K2 alias phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10039:{
      //Const is +10cm between 70N and 80N, -10cm otherwise

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant.
      k++; temp_double = 10.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) < 80.0 and grid.lat.at(i) > 70.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10040:{
      //Const is +10cm between 55N and 65N, -10cm otherwise

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant.
      k++; temp_double = 10.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) < 65.0 and grid.lat.at(i) > 55.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = -temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10041:{
      //K1 is +10cm between 55N and 65N, 0 otherwise

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //K1 Amplitude.
      k++; temp_double = 10.0;
      cout<<"K1 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) < 65.0 and grid.lat.at(i) > 55.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10042:{ //K2 is +10cm between 55N and 65N, 0 otherwise

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(k2_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //K2 Amplitude.
      k++; temp_double = 10.0;
      cout<<"K2 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) < 65.0 and grid.lat.at(i) > 55.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K2 phase
      k++; temp_double = 90.0;
      cout<<"K2 phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10043:{ //P1 is +10cm between 55N and 65N, 0 otherwise

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(p1_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //P1 Amplitude.
      k++; temp_double = 10.0;
      cout<<"P1 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) < 65.0 and grid.lat.at(i) > 55.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //P1 phase
      k++; temp_double = 90.0;
      cout<<"P1 phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10044:{ //M2 is +10cm between 55N and 65N, 0 otherwise

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(m2_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //M2 Amplitude.
      k++; temp_double = 10.0;
      cout<<"M2 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lat.at(i) < 65.0 and grid.lat.at(i) > 55.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 phase
      k++; temp_double = 90.0;
      cout<<"M2 phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10045:{
      //K1 is +10cm between 55N and 65N (and 0E and 180E), 0 otherwise

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(k1_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //K1 Amplitude.
      k++; temp_double = 10.0;
      cout<<"K1 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lat.at(i) < 65.0 and grid.lat.at(i) > 55.0) and ((grid.lon.at(i) < 180.0 and grid.lon.at(i) > 0.0)))
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10046:{ //K1 is +10cm everywhere.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(k1_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //K1 Amplitude.
      k++; temp_double = 10.0;
      cout<<"K1 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      //K1 phase
      k++; temp_double = 90.0;
      cout<<"K1 phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10047:{ //M2 is +10cm everywhere.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(m2_period);
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //M2 Amplitude.
      k++; temp_double = 10.0;
      cout<<"M2 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
          results.outputs.at(k).at(i) = temp_double;
      }

      //M2 phase
      k++; temp_double = 90.0;
      cout<<"M2 phase is "<<temp_double<<" everywhere."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10048:{ //Constant is +10cm everywhere.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Constant
      k++; temp_double = 10.0;
      cout<<"Constant is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
          results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10049:{ //Trend is +10cm/year everywhere.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Trend
      k++; temp_double = 10.0;
      cout<<"Trend is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.at(k).at(i) = temp_double;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10050:{
      //Copy of 10023, but no constant, M2 and K1 are identical, sin/cos.
      //Parameters - m2, k1. Amps = 10cm.
      //M2,K1  - phase = lon-dep (constant in each quadrant) - only in ocean!
      //Here ocean is defined using wcover > 0.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;//INITIALLY in amp/phase format!
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //M2 Amplitude.
      k++; temp_double = 10.0;
      cout<<"M2 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean!
        if(grid.wcover.at(i) > 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; double temp_double2 = 90.0;
      cout<<"M2 phase is +/- "<<temp_double2<<" deg ('+' for lon 0..90,180..270, '-' for lon 90..180,270..360)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lon.at(i) < 90.0 and grid.lon.at(i) >= 0.0) or (grid.lon.at(i) < 270.0 and grid.lon.at(i) >= 180.0)){
          results.outputs.at(k).at(i) = temp_double2;
        }
        else results.outputs.at(k).at(i) = -temp_double2;
      }

      //K1 Amplitude.
      k++; 
      cout<<"K1 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean!
        if(grid.wcover.at(i) > 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++;
      cout<<"K1 phase is +/- "<<temp_double2<<" deg ('+' for lon 0..90,180..270, '-' for lon 90..180,270..360)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if((grid.lon.at(i) < 90.0 and grid.lon.at(i) >= 0.0) or (grid.lon.at(i) < 270.0 and grid.lon.at(i) >= 180.0)){
          results.outputs.at(k).at(i) = temp_double2;
        }
        else results.outputs.at(k).at(i) = -temp_double2;
      }
      
      convert_results_from_ampphase_to_sincos(results);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10051:{
      //Copy of 10023, but no constant, M2 and K1 are identical, sin/cos.
      //Parameters - m2, k1. Amps = 10cm.
      //M2,K1  - uniform amp on land only w lon-dep phase: -90 from 0-60,
      //         120-180, 240-300, +90 otherwise.
      //Here ocean is defined using mascon elevation < 0.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;//INITIALLY in amp/phase format!
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //M2 Amplitude.
      k++; temp_double = 10.0;
      cout<<"M2 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; double temp_double2 = 90.0;
      cout<<"M2 phase is +/- "<<temp_double2<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double2;
        else results.outputs.at(k).at(i) = -temp_double2;
      }

      //K1 Amplitude.
      k++; 
      cout<<"K1 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i) > 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++;
      cout<<"K1 phase is +/- "<<temp_double2<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double2;
        else results.outputs.at(k).at(i) = -temp_double2;
      }
      
      convert_results_from_ampphase_to_sincos(results);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10052:{
      //Copy of 10050, but added trend along with identical M2, K1. sin/cos.
      //Parameters - m2, k1. Amps = 10cm.
      //Trend - uniform trend on land.
      //M2,K1  - phase = (constant in quadrants NOT centered on NP - only in ocean!
      //Here ocean is defined using wcover > 0.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;//INITIALLY in amp/phase format!
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      double desired_lat = 60.0, desired_lat2 = 70.0, desired_lon = 0;

      //Trend
      k++; temp_double = 10.0;
      cout<<"Trend is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only on land.
        if(grid.wcover.at(i) == 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Amplitude.
      k++;
      cout<<"M2 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean!
        if(grid.wcover.at(i) > 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; double temp_double2 = 90.0;
      cout<<"M2 phase is +"<<temp_double2<<" deg N of "<<desired_lat2<<" N. Points south display a checkerboard pattern at "<<desired_lat<<" N and "<<desired_lon<<" E."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        double lon_diff = restrict_degrees(grid.lon.at(i) - desired_lon); 
        if((grid.lat.at(i) > desired_lat2) or (grid.lat.at(i) < desired_lat2 and grid.lat.at(i) > desired_lat and lon_diff < 0) or (grid.lat.at(i) < desired_lat and lon_diff > 0)){
          results.outputs.at(k).at(i) = temp_double2;
        }
        else results.outputs.at(k).at(i) = -temp_double2;
      }

      //K1 Amplitude.
      k++; 
      cout<<"K1 amp is "<<temp_double<<" cm."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only in ocean!
        if(grid.wcover.at(i) > 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++;
      cout<<"K1 phase is +"<<temp_double2<<" deg N of "<<desired_lat2<<" N. Points south display a checkerboard pattern at "<<desired_lat<<" N and "<<desired_lon<<" E."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        double lon_diff = restrict_degrees(grid.lon.at(i) - desired_lon); 
        if((grid.lat.at(i) > desired_lat2) or (grid.lat.at(i) < desired_lat2 and grid.lat.at(i) > desired_lat and lon_diff < 0) or (grid.lat.at(i) < desired_lat and lon_diff > 0)){
          results.outputs.at(k).at(i) = temp_double2;
        }
        else results.outputs.at(k).at(i) = -temp_double2;
      }
      
      convert_results_from_ampphase_to_sincos(results);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10053:{
      //Copy of 10051, but added trend along with identical M2, K1. sin/cos.
      //Parameters - m2, k1. Amps = 10cm.
      //Trend - uniform trend on land.
      //M2,K1  - uniform amp on land only w lon-dep phase: -90 from 0-60,
      //         120-180, 240-300, +90 otherwise.
      //Here ocean is defined using wcover > 0.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);//M2
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);//K1
        results.options.parameter_type.push_back(101);

        results.options.sincos = 0;//INITIALLY in amp/phase format!
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Trend
      k++; temp_double = 10.0;
      cout<<"Trend is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only on land.
        if(grid.wcover.at(i) == 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Amplitude.
      k++; temp_double = 10.0;
      cout<<"M2 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only on land.
        if(grid.wcover.at(i) == 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //M2 Phase.
      k++; double temp_double2 = 90.0;
      cout<<"M2 phase is +/- "<<temp_double2<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double2;
        else results.outputs.at(k).at(i) = -temp_double2;
      }

      //K1 Amplitude.
      k++; 
      cout<<"K1 amp is "<<temp_double<<" cm everywhere on land."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only on land.
        if(grid.wcover.at(i) == 0.0)
          results.outputs.at(k).at(i) = temp_double;
        else results.outputs.at(k).at(i) = 0;
      }

      //K1 phase
      k++;
      cout<<"K1 phase is +/- "<<temp_double2<<" deg ('+' for lon 60-120,180-240,300-360, '-' for lon 0-60,120-180,240-300)."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.lon.at(i) - 120.0*floor(grid.lon.at(i)/120.0) > 60.0)
          results.outputs.at(k).at(i) = temp_double2;
        else results.outputs.at(k).at(i) = -temp_double2;
      }
      
      convert_results_from_ampphase_to_sincos(results);

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10054:{
      //Parameters - const, lin, m2, k1. Amps = 10cm.
      //Const. - single pt at 60N, 0E.
      //Trend - single pt at 60N, 90E.
      //M2     - single pt at 60N, 180E.
      //K1     - single pt at 60N, 270E.
      typesincos = 10054;

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.parameter.push_back(m2_period);
        results.options.parameter_type.push_back(101);

        results.options.parameter.push_back(k1_period);
        results.options.parameter_type.push_back(101);

        if(results.options.type == typesincos) results.options.sincos = 1;
        else results.options.sincos = 0;
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.
      
      //Constant.
      k++; temp_double = 10.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 0.0, verbose);
      cout<<"Closest matching grid point has lat "<<grid.lat.at(index)<<" and lon "<<grid.lon.at(index)<<endl;
      results.outputs.at(k).at(index) = temp_double;

      //Trend.
      k++; temp_double = 10.0;
      cout<<"Trend's simulated term is "<<temp_double<<" cm/year."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 90.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //M2 Amplitude.
      k++; temp_double = 10.0;
      cout<<"M2 amp is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 180.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //M2 Phase.
      k++; temp_double = 0.0;
      cout<<"M2 phase is "<<temp_double<<" deg."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 180.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //K1 Amplitude.
      k++; temp_double = 10.0;
      cout<<"K1 amp is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 270.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //K1 Phase.
      k++; temp_double = 0.0;
      cout<<"K1 phase is "<<temp_double<<" deg."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, 60.0, 270.0, verbose);
      results.outputs.at(k).at(index) = temp_double;

      //If necessary, convert to sincos
      if(results.options.type == typesincos){
        convert_results_from_ampphase_to_sincos(results);
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10055:{//Secular trend of 10 cm/year over land, defined using wcover == 0.

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Trend
        results.options.parameter_type.push_back(1);

        results.options.sincos = 0;//INITIALLY in amp/phase format!
        results.options.uses_least_squares = 0;
        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      //Trend
      k++; temp_double = 10.0;
      cout<<"Trend is "<<temp_double<<" cm/year."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Only on land.
        if(grid.wcover.at(i) == 0.0){
          results.outputs.at(k).at(i) = temp_double;
        }
        else results.outputs.at(k).at(i) = 0;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10056:{
      //Constant 10cm at single pt at 62N, 35E

      if(results.options.inside_load_options==1){
        //parameter/type vectors are usually filled with irrelevant values.
        results.options.parameter.clear();
        results.options.parameter_type.clear();

        results.options.parameter.push_back(0);//Constant
        results.options.parameter_type.push_back(0);

        results.options.sincos = 0;//INITIALLY in amp/phase format!
        results.options.uses_least_squares = 0;

        return;
      }
      k=-1;//Start index here so all code blocks simply set k++.

      double desired_lat=62.0,desired_lon=35.0;

      //Constant.
      k++; temp_double = 10.0;
      cout<<"Constant's simulated term is "<<temp_double<<" cm."<<endl;
      //Find grid pt closest to ideal location listed above.
      index = grid_index(grid, desired_lat, desired_lon, verbose);
      cout<<"Closest matching grid point has lat "<<grid.lat.at(index)<<" and lon "<<grid.lon.at(index)<<endl;
      results.outputs.at(k).at(index) = temp_double;

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////ANCILLARY DATA//////////////////////////////
    //////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 20001:{//Elevation in meters.

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.parameter.push_back(0);
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      sprintf(str,"Elevation and Bathymetry");
      results.titles.push_back(str);
      results.units.push_back("meters");
      results.outputs.front().resize(grid.lat.size(),0.0);
      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){
        results.outputs.front().at(i) = grid.elev.at(i);
      }
      //Unless changed, all types convert from input.epoch to J2000.
      results.options.adjust_phases_choice = -1;
      results.options.current_phase_format = 1;
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 20002:{//Pos elev=10, neg elev = 0. To see coastlines better.

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.parameter.push_back(0);
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        return;
      }

      //This case over-writes titles and units.
      results.titles.clear();
      results.units.clear();

      sprintf(str,"Neg elev = 0, pos elev = 10");
      results.titles.push_back(str);
      results.units.push_back("N/A");
      results.outputs.front().resize(grid.lat.size(),0.0);
      //Loop through bins.
      for(i=0;i<(long long)grid.lat.size();i++){
        if(grid.elev.at(i)<=0) results.outputs.front().at(i) = 0;
        else results.outputs.front().at(i) = 10;
      }
      break;
    }
    //////////////////////////////////////////////////////////////////////
    ////////////////////////////INSITU DATA///////////////////////////////
    //////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 30001:{//Tide gauge locations

      if(results.options.inside_load_options==1){
        results.options.parameter.clear();
        results.options.parameter_type.clear();
        results.options.parameter.push_back(0);
        results.options.parameter_type.push_back(0);
        results.options.sincos = 0;
        results.options.uses_least_squares = 0;
        //Override the usual map output.
        results.options.output_choice = 4;
        return;
      }

      //This case over-writes titles; units aren't even used.
      results.titles.clear();

      //It also over-writes the grid.
      grid = insitu.tg.grid;

      sprintf(str,"Tide gauge locations");
      results.titles.push_back(str);

      //Labels are the same as TG titles.
      results.labels.clear();
      results.labels.resize(1);
      results.labels.front() = insitu.tg.titles;

      results.outputs.front().resize(grid.lat.size(),0.0);

      //No need to actually perform in situ comparison.
      insitu.comparison_type = 0;
      break;
    }
  }//End of switch-case statement.

  //Unless changed, all types convert from input.epoch to J2000.
  //Anything over 10000 is a simulation or ancillary plot (RIGHT?)
  //so it shouldn't be modified.
  if(results.options.type >= 10000){
    results.options.adjust_phases_choice = -1;
  }
  adjust_phases(input, results);
}

void perform_timeseries_analysis(input_s &input, results_s &results){
  /**********************************************************************
  Purpose: This function analyzes the input timeseries, but creates
           plots rather than maps as with perform_analysis().
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long i,j,k,numswaths=20;
  long min_swath_pts = 240;//Any fewer points and this isn't a swath.
  
  double delta = input.time.at(1)-input.time.front();
  if(delta > 5) cout<<"Note: delta = "<<delta<<endl;
  
  char s[max_length];

  vector<double> temp_v_double;//Used to push_back vectors of dbl vectors
  vector< vector<double> > temp_vv_double;//push_back vs of vs of dbl vectors
  vector<string> temp_v_string;//push_back vs of string vectors
  
  //Holds indices of pts in current "swath candidate"
  vector<long> current_swath_indices;

  //Keep options, but delete anything else in results struct.
  results_s empty_results;
  empty_results.options = results.options;
  results = empty_results;

  //Perform different types of timeseries analyses based on "type".
  switch(results.options.type){
    //////////////////////////////////////////////////////////////////////
    //Time domain.
    //////////////////////////////////////////////////////////////////////
        
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{ //Plots first N swath timeseries in different windows (hardcoded)
      
      //Loop until numswaths have been recorded or end of input is reached.
      i=0;//No swaths recorded
      j=1;//Starting on second input entry so that c_s_i has at least 1 entry.
      current_swath_indices.push_back(0);
      while(i<numswaths and j<(long long)input.time.size()){
      
        //Is the next point too far away to be in the same swath, or is there NO next point?
        if(input.time.at(j) - input.time.at(current_swath_indices.back()) > 5 or j==(long long)input.time.size()-1){
          //If so, have enough points been found for this to be a swath?
          if((long)current_swath_indices.size() >= min_swath_pts){
            
            //Save misc info for this plot.
            sprintf(s,"Swath #%3ld",i+1);
            results.xy.titles.push_back(s);
            results.xy.x_units.push_back("seconds");
            results.xy.y_units.push_back("m/s^2");
            
            //This is a brand new plot, so push_back using the new vv.
            results.xy.x_values.push_back(temp_vv_double);
            results.xy.y_values.push_back(temp_vv_double);
            results.xy.legends.push_back(temp_v_string);
            
            //There's also just 1 dataseries per plot, so push_back now.
            results.xy.x_values.back().push_back(temp_v_double);
            results.xy.y_values.back().push_back(temp_v_double);
            
            //Now resize the vector for the 0th dataseries of the i'th plot
            results.xy.x_values.back().front().resize((long)current_swath_indices.size());
            results.xy.y_values.back().front().resize((long)current_swath_indices.size());
            
            //Save individual XY values for this plot.
            double rms = 0.0;
            for(k=0;k<(long)current_swath_indices.size();k++){
              results.xy.x_values.back().front().at(k) = input.time.at(current_swath_indices.at(k))-input.time.at(current_swath_indices.front());
              results.xy.y_values.back().front().at(k) = input.accel.at(current_swath_indices.at(k));
              rms += pow(input.accel.at(current_swath_indices.at(k)),2);
            }

            rms = sqrt(rms/(double)current_swath_indices.size());
            
            results.xy.legends.back().push_back("RMS: ");
            sprintf(s,"%4.1e",rms);
            results.xy.legends.back().back().append(s);
            i++;
          }
          //Either way, the last swath is gonna get cleared.
          current_swath_indices.clear();
          current_swath_indices.push_back(j);
        }
        else{//If index j is in the current swath, add it to the list.
          current_swath_indices.push_back(j);
        }
        j++;//Next input point.
      }//End of loop through input.
      
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 2:{

      //Size of the chunks derived from range and number of bins.
      double chunksize=results.options.regparam1*2/(double)results.options.nmax;
      int numchunks = results.options.nmax;
      cout<<"Accels are being grouped into "<<numchunks<<" bins (called chunks in code for legacy reasons) of size "<<chunksize<<" nm/s^2 from +/- "<<results.options.regparam1<<" nm/s^2."<<endl;

      results.xy.titles.push_back(input.options.filename.c_str());
      results.xy.x_units.push_back("Accel (nm/s^2)");
      results.xy.y_units.push_back("#");

      //This is a brand new plot, so push_back using temp vectors/vvs.
      results.xy.x_values.push_back(temp_vv_double);
      results.xy.y_values.push_back(temp_vv_double);

      //This plot has 1 line in it.
      results.xy.x_values.front().resize(1);
      results.xy.y_values.front().resize(1);

      //That line has "nmax" data points.
      results.xy.x_values.front().front().resize(numchunks);
      results.xy.y_values.front().front().resize(numchunks);

      long long current_chunk;
      vector <long long> num_in_chunk;
      num_in_chunk.clear();
      //Records how many accels were greater than the maximum, less than min.
      long long over_limit=0, under_limit=0;
      num_in_chunk.resize(numchunks,0);
      for(k=0;k<(long long)10;k++) cout<<setprecision(15)<<setw(20)<<input.accel.at(k)*1E9<<endl;

      //Loop through all accels.
      for(k=0;k<(long long)input.accel.size();k++){
        //What chunk does this accel fall into?
        current_chunk = (long long)floor((input.accel.at(k)*1E9 + results.options.regparam1)/chunksize);

        //If the index is too big, this time diff is > max_time.
        if(current_chunk >= numchunks) over_limit++;
        else if(current_chunk < 0) under_limit++;
        else num_in_chunk.at(current_chunk)++;
      }//End of loop that goes through input file.

      //Save individual XY values for this particular param's line.
      for(k=0;k<(long long)numchunks;k++){
        //X-value is the MIDDLE of the chunk.
        results.xy.x_values.front().front().at(k) = ((double)k+0.5)*chunksize-results.options.regparam1;
        results.xy.y_values.front().front().at(k) = num_in_chunk.at(k);
      }
      if(over_limit > 0) cout<<over_limit<<" accels above "<<results.options.regparam1<<" nm/s^2."<<endl;
      if(under_limit > 0) cout<<under_limit<<" accels below "<<-results.options.regparam1<<" nm/s^2."<<endl;
      break;
    }

    //////////////////////////////////////////////////////////////////////
    //Spectral domain.
    //////////////////////////////////////////////////////////////////////

    //Requires GSL libraries.
    #ifdef GSL_HERE
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1000:{ //Tests FT power spectra by simulating many sinusoids.
      
      numswaths=20;
      long swath_size = 250;//253;//Matches most input swaths.
      double data[10000];//Serious problem!!
      
      double max_freq = 0.05;//In Hz.

      gsl_fft_real_wavetable * real;
      gsl_fft_real_workspace * work;
 
      //I've got to initialize this otherwise I get a warning- no other reason
      gsl_complex_packed_array complex_coefficient=data;
      
      vector<double> current_powers,current_freqs;
      
      double current_frequency;//Hz.

      //Set up GSL temp variables.
      work = gsl_fft_real_workspace_alloc(swath_size);
      real = gsl_fft_real_wavetable_alloc(swath_size);            
  
      //Loop through various sinusoidal simulations.
      for(i=0;i<numswaths;i++){
        //Max freq I care about is 0.05 Hz, where input power spectra is 0.
        current_frequency = (max_freq/(double)numswaths)*(double)(i+1);
        
        current_powers.clear(); current_freqs.clear();

        //Create simulated accels for the data array.
        for(k=0;k<swath_size;k++) data[k] = cos(twoPi*current_frequency*delta*k);
        
        //Perform an FFT on the input accels, which are real numbers.
        gsl_fft_real_transform (data, 1, swath_size, real, work);

        gsl_fft_halfcomplex_unpack (data, complex_coefficient, 1, swath_size);

        //Save individual XY values for this plot.
        //Only loop to the middle of the number of input points in the
        //swath, because after that point the negative frequencies start
        //and they're just complex conjugates of the previous ones.
        //Or... is it N/2 because the real/imag parts are stored in an
        //alternating fashion?  Grrr...
        //
        //Skipping index 0 because that's the zeroth frequency component.
        for(k=1;k<(swath_size)/2;k++){
             
          //Only push_back if the frequency is low enough that I think
          //there should be some signal.  20 seconds period = 0.05 Hz
          if(k/(delta*swath_size) <= max_freq){
            current_powers.push_back(sqrt(pow(complex_coefficient[2*k],2)+ pow(complex_coefficient[2*k+1],2)));
            current_freqs.push_back(k/(delta*swath_size));
          }
        }
        
        //Save misc info for this plot.
        sprintf(s,"Power for sim at freq. %f",current_frequency);
        results.xy.titles.push_back(s);
        results.xy.x_units.push_back("Hz");
        results.xy.y_units.push_back("Power");
        
        //This is a brand new plot, so push_back using the new vv.
        results.xy.x_values.push_back(temp_vv_double);
        results.xy.y_values.push_back(temp_vv_double);
        
        //There's also just 1 dataseries per plot, so push_back now.
        results.xy.x_values.back().push_back(current_freqs);
        results.xy.y_values.back().push_back(current_powers);
        
      }//End of loop through simulations.
      
      //Free up memory taken up by GSL temp vars.
      gsl_fft_real_wavetable_free (real);
      gsl_fft_real_workspace_free (work);    
 
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1001:{ //Plots first N swath FTs in different windows (hardcoded)
      
      double data[10000];//Serious problem!!
      
      double max_freq = 0.05;//In Hz.
      
      gsl_fft_real_wavetable * real;
      gsl_fft_real_workspace * work;
 
      //I've got to initialize this otherwise I get a warning- no other reason
      gsl_complex_packed_array complex_coefficient=data;

      //Loop until numswaths have been recorded or end of input is reached.
      i=0;//No swaths recorded
      j=1;//Starting on second input entry so that c_s_i has at least 1 entry.
      current_swath_indices.push_back(0);
      while(i<numswaths and j<(long long)input.time.size()){
      
        //Is the next point too far away to be in the same swath, or is there NO next point?
        if(input.time.at(j) - input.time.at(current_swath_indices.back()) > 5 or j==(long long)input.time.size()-1){
          //If so, have enough points been found for this to be a swath?
          if((long)current_swath_indices.size() >= min_swath_pts){
            cout<<"Swath #"<<i+1<<" has "<<(long)current_swath_indices.size()<<" points, starting at time "<<input.time.at(current_swath_indices.front())+input.epoch<<endl;
            //Save misc info for this plot.
            sprintf(s,"Swath #%3ld",i+1);
            results.xy.titles.push_back(s);
            results.xy.x_units.push_back("Hz");
            results.xy.y_units.push_back("Power");
            
            //This is a brand new plot, so push_back using the new vv.
            results.xy.x_values.push_back(temp_vv_double);
            results.xy.y_values.push_back(temp_vv_double);
            
            //There's also just 1 dataseries per plot, so push_back now.
            results.xy.x_values.back().push_back(temp_v_double);
            results.xy.y_values.back().push_back(temp_v_double);

            //Set up GSL temp variables.  Needs to be allocated in loop b/c
            //the size is potentially different each time.
            work = gsl_fft_real_workspace_alloc ((long)current_swath_indices.size());
            real = gsl_fft_real_wavetable_alloc ((long)current_swath_indices.size());
            
            //Copy accels to the data array.
            for(k=0;k<(long)current_swath_indices.size();k++) data[k] = input.accel.at(current_swath_indices.at(k));

            //Perform an FFT on the input accels, which are real numbers.
            gsl_fft_real_transform (data, 1, (long)current_swath_indices.size(), real, work);

            gsl_fft_halfcomplex_unpack (data, complex_coefficient, 1, (long)current_swath_indices.size());
            
            //int gsl_fft_halfcomplex_unpack (const double halfcomplex_coefficient[], gsl_complex_packed_array complex_coefficient, size_t stride, size_t n)

    //This function converts halfcomplex_coefficient, an array of half-complex coefficients as returned by gsl_fft_real_transform, into an ordinary complex array, complex_coefficient. It fills in the complex array using the symmetry z_k = z_{N-k}^* to reconstruct the redundant elements. The algorithm for the conversion is,
            
            //Free up memory taken up by GSL temp vars.
            //Needs to be done each time because size changes each loop!
            gsl_fft_real_wavetable_free (real);
            gsl_fft_real_workspace_free (work);    
            
            /*//Save individual XY values for this plot.
            for(k=0;k<(long)current_swath_indices.size();k++){
              results.xy.x_values.back().front().at(k) = input.time.at(current_swath_indices.at(k));

              //To perform an FFT on a vector argument, such as gsl_vector_complex * v, use the following definitions (or their equivalents) when calling the functions described in this chapter:

     //gsl_complex_packed_array data = v->data;
     //size_t stride = v->stride;
     //size_t n = v->size;

              results.xy.y_values.back().front().at(k) = data[k];
            }//*/
            
            //Save individual XY values for this plot.
            //Only loop to the middle of the number of input points in the
            //swath, because after that point the negative frequencies start
            //and they're just complex conjugates of the previous ones.
            //Or... is it N/2 because the real/imag parts are stored in an
            //alternating fashion?  Grrr...
            //
            //Skipping index 0 because that's the zeroth frequency component.
            for(k=1;k<((long)current_swath_indices.size())/2;k++){
              
              //Only push_back if the frequency is low enough that I think
              //there should be some signal.  20 seconds period = 0.05 Hz
              if(k/(delta*(long)current_swath_indices.size()) <= max_freq){
                results.xy.x_values.back().front().push_back(k/(delta*(long)current_swath_indices.size()));
                results.xy.y_values.back().front().push_back(sqrt(pow(complex_coefficient[2*k],2)+ pow(complex_coefficient[2*k+1],2)));
              }
            }
            
            i++;//Just recorded a new swath.
          }
          //else cout<<"This little swath only has "<<(long)current_swath_indices.size()<<" points, starting at time "<<input.time.at(current_swath_indices.front())+input.epoch<<endl;
          //Either way, the last swath is gonna get cleared.
          current_swath_indices.clear();
          current_swath_indices.push_back(j);
        }
        else{//If index j is in the current swath, add it to the list.
          current_swath_indices.push_back(j);
        }
        j++;//Next input point.
      }//End of loop through input.
      
      //gsl_fft_halfcomplex_wavetable * hc;
      //hc = gsl_fft_halfcomplex_wavetable_alloc (n);
      //gsl_fft_halfcomplex_inverse (data, 1, n, hc, work);
      //gsl_fft_halfcomplex_wavetable_free (hc);
      
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1010:{ //Plots Avg power
      numswaths=100000;
      double data[100000];//Serious problem!!
      
      double max_freq = 0.25;//In Hz.

      long long desired_size = 249;
      
      gsl_fft_real_wavetable * real;
      gsl_fft_real_workspace * work;
 
      //I've got to initialize this otherwise I get a warning- no other reason
      gsl_complex_packed_array complex_coefficient=data;
      
      vector<double> current_powers,average_powers,current_freqs, hist_of_max_powers;

      //Loop until numswaths have been recorded or end of input is reached.
      i=0;//No swaths recorded
      j=1;//Starting on second input entry so that c_s_i has at least 1 entry.
      current_swath_indices.push_back(0);
      while(i<numswaths and j<(long long)input.time.size()){
      
        //Is the next point too far away to be in the same swath, or is there NO next point?
        if(input.time.at(j) - input.time.at(current_swath_indices.back()) > delta or j==(long long)input.time.size()-1){
          //If so, does this swath have the exact correct number of points?
          //This pickiness is necessary because I'm averaging over each
          //Fourier transform.
          if((long)current_swath_indices.size() >= desired_size){
            //cout<<"Swath #"<<i+1<<" has "<<(long)current_swath_indices.size()<<" points."<<endl;
            
            current_powers.clear(); current_freqs.clear();

            //Set up GSL temp variables.  Needs to be allocated in loop b/c
            //the size is potentially different each time.
            work = gsl_fft_real_workspace_alloc ((long)desired_size);
            real = gsl_fft_real_wavetable_alloc ((long)desired_size);
            
            //Copy accels to the data array after converting to nm/s^2.
            for(k=0;k<(long)desired_size;k++) data[k] = input.accel.at(current_swath_indices.at(k))*1E9;

            //Perform an FFT on the input accels, which are real numbers.
            gsl_fft_real_transform (data, 1, (long)desired_size, real, work);

            gsl_fft_halfcomplex_unpack (data, complex_coefficient, 1, (long)desired_size);
            
            //Free up memory taken up by GSL temp vars.
            //Needs to be done each time because size changes each loop!
            gsl_fft_real_wavetable_free (real);
            gsl_fft_real_workspace_free (work);    
            
            //Save individual XY values for this plot.
            //Only loop to the middle of the number of input points in the
            //swath, because after that point the negative frequencies start
            //and they're just complex conjugates of the previous ones.
            //Or... is it N/2 because the real/imag parts are stored in an
            //alternating fashion?  Grrr...
            //
            //Skipping index 0 because that's the zeroth frequency component.
            for(k=1;k<((long)desired_size)/2;k++){
              
              //Only push_back if the frequency is low enough that I think
              //there should be some signal.  20 seconds period = 0.05 Hz
              if(k/(delta*(long)desired_size) <= max_freq){
                current_powers.push_back(sqrt(pow(complex_coefficient[2*k],2)+ pow(complex_coefficient[2*k+1],2)));
                current_freqs.push_back(k/(delta*(long)desired_size));
              }
            }
            
            if(i==0){
              average_powers=current_powers;
              hist_of_max_powers.resize((long)desired_size,0);
            }
            else{
              for(k=0;k<((long)current_powers.size());k++) average_powers.at(k) += current_powers.at(k);
            }
            
            //use current max power's frequency to increment histogram.
            double maxvalue = 0.0;
            long maxindex = -1;
            for(k=0;k<((long)current_powers.size());k++){
              if(maxvalue<current_powers.at(k)){
                maxvalue=current_powers.at(k);
                maxindex = k;
              }
            }
            hist_of_max_powers.at(maxindex) +=1;
            
            i++;//Just recorded a new swath.
          }
          //else cout<<"Nope, this swath has "<<current_swath_indices.size()<<" points."<<endl;
          //Either way, the last swath is gonna get cleared.
          current_swath_indices.clear();
          current_swath_indices.push_back(j);
        }
        else{//If index j is in the current swath, add it to the list.
          current_swath_indices.push_back(j);
        }
        j++;//Next input point.
      }//End of loop through input.
      
      //convert sum to average.
      for(k=0;k<((long)current_powers.size());k++) average_powers.at(k) /= (double)i;
 
      //Save misc info for this plot.
      sprintf(s,"Power averaged over %ld swaths",i);
      results.xy.titles.push_back(s);
      results.xy.x_units.push_back("Hz");
      results.xy.y_units.push_back("Average Power");
            
      //This is a brand new plot, so push_back using the new vv.
      results.xy.x_values.push_back(temp_vv_double);
      results.xy.y_values.push_back(temp_vv_double);
            
      //There's also just 1 dataseries per plot, so push_back now.
      results.xy.x_values.front().push_back(current_freqs);
      results.xy.y_values.front().push_back(average_powers);
      
      //Save misc info for this plot.
      results.xy.titles.push_back("Histogram - frequency of maxima");
      results.xy.x_units.push_back("Hz");
      results.xy.y_units.push_back("# times max was here");
            
      //This is a brand new plot, so push_back using the new vv.
      results.xy.x_values.push_back(temp_vv_double);
      results.xy.y_values.push_back(temp_vv_double);
            
      //There's also just 1 dataseries per plot, so push_back now.
      results.xy.x_values.at(1).push_back(current_freqs);
      results.xy.y_values.at(1).push_back(hist_of_max_powers);
      
      break;
    }
    //////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////
    case 2000:{ //Tests hi,lopass square filter by sending sines w/ diff frqs

      numswaths=20;
      long swath_size = 253;//Matches most input swaths.
      double data[10000];//Serious problem!!
      
      double max_freq = 0.02;//In Hz.

      gsl_fft_real_wavetable * real;
      gsl_fft_real_workspace * work;
 
      double current_frequency;//Hz.

      //Set up GSL temp variables.
      work = gsl_fft_real_workspace_alloc(swath_size);
      real = gsl_fft_real_wavetable_alloc(swath_size);            
  
      //Loop through various sinusoidal simulations.
      for(i=0;i<numswaths;i++){
        current_frequency = (max_freq/(double)numswaths)*(double)(i+1);

        //Create simulated accels for the data array.
        for(k=0;k<swath_size;k++) data[k] = cos(twoPi*current_frequency*delta*k);
        
        //Perform an FFT on the input accels, which are real numbers.
        gsl_fft_real_transform (data, 1, swath_size, real, work);
        
        vector<double> fourier_frequencies(swath_size);
        for(k=0;k<swath_size;k++){
          if(k % 2 == 0){
            fourier_frequencies.at(k) = (double)(k/2)/(double)(swath_size*delta);
            //cout<<"k="<<k<<", (even) and freq="<<fourier_frequencies.at(k)<<""<<endl;
          }
          else{
            fourier_frequencies.at(k) = (double)((k+1)/2)/(double)(swath_size*delta);
            //cout<<"k="<<k<<", (odd) and freq="<<fourier_frequencies.at(k)<<""<<endl;
          }
        }  

        //lo pass filter
        //results.options.cutoff_freq
        //need to make vec that has freq of all entries in data, even or odd
        //if(k/(delta*(long)current_swath_indices.size()) <= 0.05){
        for(k=0;k<swath_size;k++) if(fourier_frequencies.at(k) >= results.options.cutoff_freq) data[k] = 0;
        
        //change back to time series.

        gsl_fft_halfcomplex_wavetable * hc;
        hc = gsl_fft_halfcomplex_wavetable_alloc (swath_size);
     
        gsl_fft_halfcomplex_inverse (data, 1, swath_size, hc, work);
        gsl_fft_halfcomplex_wavetable_free (hc);

        //write data back to input.accel, figure out some way to save a vector
        //of longs that says which input points are being kept and not kept.
        
        //Save misc info for this plot.
        sprintf(s,"Sim at %f Hz (cutoff %f Hz)", current_frequency, results.options.cutoff_freq);
        results.xy.titles.push_back(s);
        results.xy.x_units.push_back("seconds");
        results.xy.y_units.push_back("nm/s^2");
        
        //This is a brand new plot, so push_back using the new vv.
        results.xy.x_values.push_back(temp_vv_double);
        results.xy.y_values.push_back(temp_vv_double);
        
        //There's also just 1 dataseries per plot, so push_back now.
        results.xy.x_values.back().push_back(temp_v_double);
        results.xy.y_values.back().push_back(temp_v_double);
            
        //Now resize the vector for the 0th dataseries of the i'th plot
        results.xy.x_values.back().front().resize(swath_size);
        results.xy.y_values.back().front().resize(swath_size);
            
        //Save individual XY values for this plot.
        for(k=0;k<swath_size;k++){
          results.xy.x_values.back().front().at(k) = delta*k;
          results.xy.y_values.back().front().at(k) = data[k];
        }
        
      }//End of loop through simulations.
      
      //Free up memory taken up by GSL temp vars.
      gsl_fft_real_wavetable_free (real);
      gsl_fft_real_workspace_free (work);    
 
      break;
    }
    #else 
      cout<<"FFT requires GSL. Search definitions.cpp for GSL_HERE."<<endl;
    #endif
  }//End of switch-case-break  
}

//Requires GSL libraries.
#ifdef GSL_HERE
void copy_x_to_outputs(results_s &results, grid_s &grid, gsl_vector * x){
  /**********************************************************************
  Purpose: This function copies the gsl vector x to outputs.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          x - gsl_vector containing the results of solving Ax=b.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k;

  //DEBUG
  if(1==2){
    cout<<"Debug in copy_x_to_outputs()."<<endl;
    long long visible_elements=20;
    if(visible_elements > (long long)(grid.lat.size()*results.options.output_type.size()))
      visible_elements = grid.lat.size()*results.options.output_type.size();

    for(i=0;i<visible_elements;i++){
      cout<<"x["<<i<<"] = "<<gsl_vector_get(x, i)<<endl;
    }
  }

  //Loop through grid points.
  for(i=0;i<(long long)grid.lat.size();i++){
    j=0;
    for(k=0;k<(long long)results.options.parameter.size();k++){
      //New parameter types should be added here.
      if(results.options.parameter_type.at(k) < 100){
        results.outputs.at(j).at(i) = gsl_vector_get(x,i*results.options.output_type.size()+j);
        j++;
      }
      else if(results.options.parameter_type.at(k) == 101){
        //Copy the results to outputs depending on analysis type.
        if(results.options.sincos == 1){//Just copy sin/cos values.
          //Cosine coefficient is listed first.
          results.outputs.at(j).at(i) = gsl_vector_get(x,i*results.options.output_type.size()+j);
          //Sine coefficient is listed second.
          results.outputs.at(j+1).at(i) = gsl_vector_get(x,i*results.options.output_type.size()+j+1);
          j+=2;//This parameter results in TWO entries in "x".
        }
        else if(results.options.sincos == 0){//Convert to amp/phase.
          //Amplitude first.
          results.outputs.at(j).at(i) = sqrt(pow(gsl_vector_get(x,i*results.options.output_type.size()+j),2)+pow(gsl_vector_get(x,i*results.options.output_type.size()+j+1),2));
          //Phase next.
          results.outputs.at(j+1).at(i) = rad2deg*atan2(gsl_vector_get(x,i*results.options.output_type.size()+j+1),gsl_vector_get(x,i*results.options.output_type.size()+j));
          j+=2;//This parameter results in TWO entries in "x".
        }
      }//End of sinusoids.
      else if(results.options.parameter_type.at(k) >= 10000 and results.options.parameter_type.at(k) < 20000){
        results.outputs.at(j).at(i) = gsl_vector_get(x,i*results.options.output_type.size()+j);
        j++;
      }
      else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.4"<<endl;
    }//End of loop through parameters.
  }//End of loop through grid points.

  //DEBUG
  if(1==2){
    cout<<"Debug in copy_x_to_outputs()."<<endl;
    long long visible_elements=20;
    if(visible_elements > (long long)results.outputs.front().size())
      visible_elements = (long long)results.outputs.front().size();

    for(i=0;i<visible_elements;i++){
      cout<<"outputs[0]["<<i<<"] = "<<results.outputs.front().at(i)<<endl;
    }
  }
}

void copy_x_to_ampphase(gsl_vector * x,
                        results_s &ar_series1,
                        insitu_s &insitu,
                        long long i){
  /**********************************************************************
  Purpose: This function copies the gsl vector x to ampphase format.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          x - gsl_vector containing the results of solving Ax=b.
          i is the current station.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long k,l;

  int npar = (int)insitu.tg.model.periods.size()*2;

  //Resize first dimension of outputs so it can hold all model
  //periods.
  ar_series1.outputs.resize(npar);

  //Now resize the second dimension (have to do this multiple times)
  //so that the second dimension holds all stations.
  for(k=0;k<(long long)npar;k++){
    ar_series1.outputs.at(k).resize(insitu.tg.titles.size(),0.0);
  }

  l=0;
  for(k=0;k<(long long)insitu.tg.model.periods.size();k++){
    //Amplitude first.
    ar_series1.outputs.at(l).at(i) = sqrt(pow(gsl_vector_get(x,l),2)+pow(gsl_vector_get(x,l+1),2));
    //Phase next.
    ar_series1.outputs.at(l+1).at(i) = rad2deg*atan2(gsl_vector_get(x,l+1),gsl_vector_get(x,l));
    l+=2;//Amp and phase, so increment counter by 2.
  }//End of loop that cycles through parameters.
}
#endif

input_s load_regional_input(long long regionindex,
                            input_s &maininput,
                            grid_s &region){
  /**********************************************************************
  Purpose: This function loads input data only in the current region. It
           takes "input" as its second argument, but the function refers to
           it as "maininput" because the entire point of the function is to
           return a input structure that will be stored in region.input.
           This input structure (the one that holds the regional input data)
           is referred to inside the function as "input", but it is assigned
           to region.input in main(). This function extracts input data
           in this region from disk and saves it in the input structure in
           the same way that old code would have stored ALL the input
           data. It also makes a copy of the input.binindices vector, the
           vector that keeps track of which bin each input point falls into-
           but this copy of input.binindices only has entries for the input
           points in the current region (denoted by regionindex).
  ***********************************************************************
  Input:   (types ending in "_s" are defined in definitions.hpp)
           int regionindex - The current region to load. Runs from 0
                             to region.lat.size()-1. Usually this is
                             the counter variable from the loop in main()
                             that calls load_regional_input.
  ***********************************************************************
  Output:  (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  input_s input;
  double onelat,onelon,oneaccel,onexA,onexB,oneyA,oneyB,onezA,onezB;
  long long onetime,i,j,k;
  int oneascending;
  char s[max_length];
  string basefolder(gaiafolder());
  string file;
  //For reading arbitrary data.
  string file2, line1;
  ifstream input_file;//Used to grab strings from the file.
  istringstream stream1;//Used to grab data from the strings.
  string filename;
  double temp_double;
  long long temp_long;

  //These variables are used to load input data only in the current region.
  int inregion=0;//0 if input point is not in region, 1 if it is in region.
  int inregionprevious=0;//1 if last input point was in region, 0 otherwise.
  int tempcounter=0;//Holds the number of consecutive points in/out of region.
  //readinput is a vector that is used to selectively load data from the
  //input file.  If the value is positive (negative), a for-do loop is
  //entered that reads and saves (ignores) the input data in those lines.
  vector<int> readinput;

  //I'm making a copy of binindices which holds the bin index for each input
  //pt, but it's the bin index with respect to the grid points defined in
  //the region.

  vector<long long>::iterator iri;//Used to search binindices vector.
  long long inregionindex;//Contains iterator converted to long long.

  char* f_return;//Used to stop fgets return value warning.

  readinput.clear();//Is this really necessary?
  input.binindices.clear();//Is this really necessary?
  input.regional_binindices.clear();//This, however, really is necessary!
  //debug! (but more recently i_p_i_r is used in upward continuation.
  maininput.indices_of_points_in_region.clear();
  maininput.indices_of_points_in_region2.clear();

  cout<<"Region "<<setw(4)<<regionindex+1<<": Preparing to read regional input data..."<<endl;

  //First, search "maininput.binindices" for input points that lie
  //in this region.  Form a vector "readinput" that contains instructions
  //on how to read the input data file in an efficient manner to
  //load only input data points in the current region.
  for(i=0;i<(long long)maininput.binindices.size();i++){
    //If this binindex is in the current region, set inregion=1.
    inregion = count(region.all_grid_indices.at(regionindex).begin(),region.all_grid_indices.at(regionindex).end(),maininput.binindices.at(i));

    //Even if this binindex is inregion, it should be considered to be
    //out of the region if the input point is designated to be a
    //verification point.  This way the verif. pts. are not used to invert.
    if(maininput.verification_points.at(i)==1) inregion=0;

    //If inregion==1, push_back that value to the readinput vector.
    //If inregion==0, increment a temporary counter that records
    //the number of consecutive zeros.
    //(The preceding guidelines are complicated by the question of whether
    //the previous input binindex was in the region or not...)
    if(inregion==1){
      //debug- every point that's inregion should have its index recorded
      //also used in upward continuation - but not as a debug tool!
      maininput.indices_of_points_in_region.push_back(i);
      //A copy of input.binindices should be made for the input points
      //in each region.
      input.binindices.push_back(maininput.binindices.at(i));

      //Translate the binindices entry from the index in the main grid
      //to the index in this region's grid.
      iri = find(region.all_grid_indices.at(regionindex).begin(),region.all_grid_indices.at(regionindex).end(),maininput.binindices.at(i));
      if(iri != region.all_grid_indices.at(regionindex).end()){
        inregionindex = (long long)(iri - region.all_grid_indices.at(regionindex).begin());
        input.regional_binindices.push_back(inregionindex);
      }
      else cout<<"!!!! WARNING!!! CRITICAL ERROR 2343VJFUWE!"<<endl;

      //if(maininput.indices_of_points_in_region.size() < 50) cout<<"Index: "<<i<<" in region."<<endl;//debug!
      if(inregionprevious == 1){
        //Since the previous input point was also in this region, increment
        //the tempcounter but don't push_back because there may be more
        //consecutive points in this region.
        tempcounter++;
        if(i==(long long)maininput.binindices.size()-1) readinput.push_back(tempcounter);
      }
      else{
        //Since the previous input point was not in the region, it's time
        //to push_back the number of consecutive points not in this region.
        //The value is neg. because these input points are NOT in region.

        //Because inregionprevious is initialized as 0, the very first
        //input point (if it's in the region) would try to record the
        //"number" of "previous" input points not in the region.  Avoid
        //this by putting the push_back() command in an if statement.
        if(i>0){
          readinput.push_back(-tempcounter);
        }
        //Now, reset tempcounter.  There's already 1 input point in region.
        tempcounter = 1;
        //Also, need to note that the current point is in the region.
        inregionprevious = 1;
      }
    }//End of "if inregion == 1" statement.
    else{

      //Debug.
      //cout<<"WARNING!! HOW IS THIS POINT NOT IN THE REGION?? maininput.binindices["<<i<<"]: "<<maininput.binindices.at(i)<<endl;
      if(inregionprevious == 0){
        //Since the previous input point was also NOT in region, increment
        //the tempcounter but don't push_back because there may be more
        //consecutive points not in the region.
        tempcounter++;
        if(i==(long long)maininput.binindices.size()-1) readinput.push_back(-tempcounter);
      }
      else{
        //Since the previous input point was in the region, it's time
        //to push_back the number of consecutive points in the region.
        //The value is positive because these input points are in the region.
        if(i>0){
          readinput.push_back(tempcounter);
        }
        //Now, reset tempcounter.  There's 1 input point NOT in region.
        tempcounter = 1;
        //Also, need to note that the current point is NOT in the region.
        inregionprevious = 0;
      }
    }//End of "inregion != 1" else statement.
  }//End of loop through maininput.binindices.
  //Prepare to open the input data file.
  basefolder.append(maininput.options.basefolder);
  file = basefolder;
  file.append(maininput.options.filename);

  //Open input file for loading.
  FILE *in_fp;
  in_fp = fopen(file.c_str(),"r");

  //Warn if file doesn't open correctly.
  if(in_fp == NULL) cout << "The input data file, " << file
                         << ", failed to open."<<endl;
  else cout<<"Region "<<setw(4)<<regionindex+1<<": Loading regional data from file " << maininput.options.filename << ".."<<endl;

  //Load the first header line separately because it contains no data.
  f_return=fgets(s,max_length,in_fp);
  long long master_index = 0;//Debug!
  
  //Check to see if arbitrary parameters were requested.
  long long numarb=0;
  vector<long long> arbind;//Arbitrary indices in y_values.
  for(i=0;i<(long long)maininput.arbitrary.y_values.size();i++){
    if((long long)maininput.arbitrary.y_values.at(i).size()>0){
      numarb++;
      arbind.push_back(i);
    }
  }

  //If so, open arbitrary cache file for loading.
  if(numarb>0){
    file2 = maininput.arbitrary.cachefolder;
    filename=create_arbitrary_cache_filename(maininput);
    file2.append(filename);
    input_file.open(file2.c_str());
    //Warn if file doesn't open correctly.
    if(input_file == NULL) cout <<"The arbitrary data file, "<<file2<< ", failed to open."<<endl;
    else cout<<"Region "<<setw(4)<<regionindex+1<<": Loading arbitrary data from file "<<filename<<".."<<endl;

    //Read the number of arbitrary parameters, make sure it agrees with numarb.
    long long numarb2;
    getline(input_file,line1);
    stream1.clear();
    stream1.str(line1);
    stream1 >> numarb2;
    if(numarb != numarb2) cout<<"WARNING!!! numarb = "<<numarb<<" while numarb2 = "<<numarb2<<endl;

    //Load the second header line separately because it contains no data.
    getline(input_file,line1);
  }

  //Now use the readinput vector to read the input file.
  for(i=0;i<(long long)readinput.size();i++){
    if(readinput.at(i)>0){
      //cout<<"readinput["<<i<<"]="<<readinput.at(i)<<endl;
      //Positive values in readinput are read and saved.
      for(j=0;j<readinput.at(i);j++){
        maininput.indices_of_points_in_region2.push_back(master_index);//debug!
        //if(i<3) cout<<"Index: "<<master_index<<" in region and binindices="<<maininput.binindices.at(master_index)<<endl;//debug!
        master_index++;//debug!
        f_return=fgets(s,max_length,in_fp);
        //If this input file has no orbit data, use old loading routines:
        if(maininput.options.no_orbit_data == 1){
          sscanf(s,ACCEL_READ_FORMAT, &onetime, &onelat, &onelon,
                 &oneaccel, &oneascending);
        }//End of "if no_orbit_data == 1"
        else{
          sscanf(s,ORBIT_READ_FORMAT, &onetime, &onelat, &onelon,
                 &oneaccel, &oneascending, &onexA, &onexB,
                 &oneyA, &oneyB, &onezA, &onezB);
          input.xA.push_back(onexA);
          input.xB.push_back(onexB);
          input.yA.push_back(oneyA);
          input.yB.push_back(oneyB);
          input.zA.push_back(onezA);
          input.zB.push_back(onezB);
        }//End of "no_orbit_data != 1" else statement.
        if(numarb>0){
          getline(input_file,line1);
          stream1.clear();
          stream1.str(line1);
          stream1 >> temp_long;
          if(onetime - maininput.epoch != temp_long) cout<<"WARNING!!! input time "<<onetime - maininput.epoch<<" doesn't match arbitrary time "<<temp_long<<endl;
          for(k=0;k<numarb;k++){
            stream1 >> temp_double;
            maininput.arbitrary.y_values.at(arbind.at(k)).at(0).push_back(temp_double);
          }
        }
        input.time.push_back(onetime - maininput.epoch);
        input.lat.push_back(onelat);
        if(fabs(onelon - 360.0) < tolerance) onelon = 0;
        input.lon.push_back(onelon);
        input.accel.push_back(oneaccel);
        input.ascending.push_back(oneascending);
      }//End of loop through values to be read.
    }//End of "if readinput[i]>0" statement.
    else{
      //cout<<"readinput["<<i<<"]="<<readinput.at(i)<<endl;
      //Negative values in readinput are read and ignored!
      for(j=0;j<-readinput.at(i);j++){
        //if(i<3) cout<<"Index: "<<master_index<<" NOT in region and binindices="<<maininput.binindices.at(master_index)<<endl;//debug!
        f_return=fgets(s,max_length,in_fp);
        if(numarb>0) getline(input_file,line1);
        //cout<<"THIS LINE IS NOT IN REGION: START"<<s<<"END"<<endl;
        master_index++;//debug!
      }//End of loop through values to be ignored.
    }
  }//End of loop through readinput vector.

  fclose(in_fp);
  if(numarb>0) input_file.close();//Close the current file.

  readinput.clear();//Is this really necessary?

  //Copy maininput.options (and etc) to regional input for future reference.
  input.options = maininput.options;
  input.epoch = maininput.epoch;
  input.first_time = maininput.first_time;
  input.last_time = maininput.last_time;
  //Next one is needed in create_least_squares_in_parallel.
  input.verification_points = maininput.verification_points;

  //Remove the mean from the arbitrary time series.
  if(numarb>0){
    for(k=0;k<numarb;k++){
      double mean = 0.0;
      long long num_nonzero=0;
      for(i=0;i<(long long)maininput.arbitrary.y_values.at(arbind.at(k)).at(0).size();i++){
        if(maininput.arbitrary.y_values.at(arbind.at(k)).at(0).at(i) != 0.0){
          mean += maininput.arbitrary.y_values.at(arbind.at(k)).at(0).at(i);
          num_nonzero++;
        }
      }
      if(num_nonzero != 0){
        mean /= (double)num_nonzero;
        cout<<"Subtracting a mean of "<<scientific<<setprecision(5)<<mean<<" from "<<maininput.arbitrary.titles.at(arbind.at(k))<<endl;
        for(i=0;i<(long long)maininput.arbitrary.y_values.at(arbind.at(k)).at(0).size();i++){
          if(maininput.arbitrary.y_values.at(arbind.at(k)).at(0).at(i) != 0.0){
            maininput.arbitrary.y_values.at(arbind.at(k)).at(0).at(i) -= mean;
          }
        }
      }
      else cout<<"No nonzero arbitrary values for "<<maininput.arbitrary.titles.at(arbind.at(k))<<endl;
    }
  }
  
  //Now copy the arbitrary data.
  input.arbitrary = maininput.arbitrary;

  //Now that this vector is being used for something other than debug,
  //(namely, in the upward continuation routines), it makes sense to save
  //it to the generated and returned input structure rather than saving it
  //in the maininput parameter, which is not used inside the region code.
  input.indices_of_points_in_region = maininput.indices_of_points_in_region;

  if((long long)input.indices_of_points_in_region.size() != (long long)input.time.size()) cout<<"!!!WARNING!!!  input.indices_of_points_in_region.size() = "<<input.indices_of_points_in_region.size()<<" while input.time.size() =  "<<input.time.size()<<endl;

  cout<<"Region "<<setw(4)<<regionindex+1<<": Finished loading. Loaded "<<input.time.size()<<" input points."<<endl;
  cout<<"First regional time: "<<setw(12)<<input.time.front()+input.epoch<<", which is "<<sec2cal(input.time.front()+input.epoch)<<endl;
  cout<<"Last regional time:  "<<setw(12)<<input.time.back()+input.epoch<<", which is "<<sec2cal(input.time.back()+input.epoch)<<endl;
  cout<<"Regional duration:   "<<setw(12)<<seconds_to_string(input.time.back()-input.time.front())<<endl;

  return input;
}

void copy_regional_results(results_s &regional_results,
                           results_s &results,
                           grid_s &region,
                           grid_s &grid,
                           grid_s &regional_grid,
                           long long &i){
  /**********************************************************************
  Purpose: This function copies results of perform_analysis that have
           been saved in (temp) "regional_results" to the permanant struct
           "results".  If the counter "i" (passed by reference
           at the end of the argument list) == 0, initialize results
           by setting the vector sizes and titles and units.
  ***********************************************************************
  Input:   (types ending in "_s" are defined in definitions.hpp)
           long long i - Counter that holds the current region index.
                         Runs from 0 to region.indices_to_analyze.size()-1.
  ***********************************************************************
  Output:  (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long j,k;//Counters.

  //Use plot_options to figure out if map output is being copied, or
  //xy output for plots.
  if(results.options.output_choice == 1){//Unstructured maps

    //If this is the first loop
    //(i.e. i == 0th element of region.indices_to_analyze),
    //initialize results by setting the sizes of the arrays and
    //copying the titles/units from regional_results.  If run in parallel,
    //all loops need initializing.
    //Also, if overlap points are being copied, it's necessary to clear the
    //results structure so previous region's points don't show up in
    //subsequent region overlap plots.
    if(i==(long long)region.indices_to_analyze.front() or region.options.parallel != 0 or results.options.copy_overlap_points == 1){
      //In the case of multiple outputs, the first range of the first
      //index is the number of parameters, which should be the same
      //as regional_results.
      results.outputs.clear();
      results.outputs.resize(regional_results.outputs.size());
      //The range of the second indices are all grid.lat.size().
      for(k=0;k<(long long)results.outputs.size();k++)
        results.outputs.at(k).resize(grid.lat.size(),0.0);
      results.titles = regional_results.titles;
      results.units = regional_results.units;
      results.omegas = regional_results.omegas;
      
      //Copy entire options structure, in case output vectors changed.
      results.options = regional_results.options;

      //In parallel code, this variable is written to disk and
      //retrieved after the fork() loop using read_output. If I ever remove
      //sequential analysis, this statement can be removed. AND most of this
      //function duplicates stuff in find_and_load_parallel... right?
      results.options.current_phase_format = regional_results.options.current_phase_format;

    }//End of "if this is the first loop" statement.

    //If copy_overlap_points == 0, just copy amplitudes from "center" bins
    //in regional_results to results.  This code depends on center
    //grids pts being listed before the overlap pts!
    if(results.options.copy_overlap_points == 0){
      for(j=0;j<(long long)region.center_grid_indices.at(i).size();j++){
        //Check to see that lat/lon in regional_grid matches grid's values.
        if(grid.lat.at(region.center_grid_indices.at(i).at(j)) != regional_grid.lat.at(j)) cout<<"grid.lat["<<region.center_grid_indices.at(i).at(j)<<"] = "<<grid.lat.at(region.center_grid_indices.at(i).at(j))<<" but regional_grid.lat["<<j<<"] = "<< regional_grid.lat.at(j)<<endl;
        if(grid.lon.at(region.center_grid_indices.at(i).at(j)) != regional_grid.lon.at(j)) cout<<"grid.lon["<<region.center_grid_indices.at(i).at(j)<<"] = "<<grid.lon.at(region.center_grid_indices.at(i).at(j))<<" but regional_grid.lon["<<j<<"] = "<< regional_grid.lon.at(j)<<endl;
        for(k=0;k<(long long)regional_results.outputs.size();k++){
          results.outputs.at(k).at(region.center_grid_indices.at(i).at(j)) = regional_results.outputs.at(k).at(j);
        }
      }
    }
    //If copy_overlap_points == 1, copy amplitudes from all bins
    //in regional_results to results.
    else if(results.options.copy_overlap_points == 1){
      cout<<"Center AND overlap points are being copied to the global results structure."<<endl;
      for(j=0;j<(long long)region.all_grid_indices.at(i).size();j++){
        //Check to see that lat/lon in regional_grid matches grid's values.
        if(grid.lat.at(region.all_grid_indices.at(i).at(j)) != regional_grid.lat.at(j)) cout<<"grid.lat["<<region.all_grid_indices.at(i).at(j)<<"] = "<<grid.lat.at(region.all_grid_indices.at(i).at(j))<<" but regional_grid.lat["<<j<<"] = "<< regional_grid.lat.at(j)<<endl;
        if(grid.lon.at(region.all_grid_indices.at(i).at(j)) != regional_grid.lon.at(j)) cout<<"grid.lon["<<region.all_grid_indices.at(i).at(j)<<"] = "<<grid.lon.at(region.all_grid_indices.at(i).at(j))<<" but regional_grid.lon["<<j<<"] = "<< regional_grid.lon.at(j)<<endl;
        for(k=0;k<(long long)regional_results.outputs.size();k++){
          results.outputs.at(k).at(region.all_grid_indices.at(i).at(j)) = regional_results.outputs.at(k).at(j);
        }
      }
    }
    else cout<<"results.options.copy_overlap_points value wasn't recognized: "<<results.options.copy_overlap_points<<endl;
  }
  else if(results.options.output_choice == 2){//2D plots
      results.xy = regional_results.xy;
  }
  else if(results.options.output_choice == 3)//Separate points.
    cout<<"WARNING!!! I'M NOT SURE IF THIS CODE WORKS WITH SEPARATE POINTS MAP TYPES! IF I LATER DECIDE THAT IT DOES, ADD 0 TO THE FIRST IF STATEMENT TO MAKE IT ACTIVATE WITH SEPARATE POINTS MAPS AS WELL!"<<endl;
  else cout<<"WARNING!!! results.options.output_choice "<<results.options.output_choice<<" wasn't recognized!"<<endl;
}

void copy_results_from_global_to_regional(
                         results_s &regional_results,
                         results_s &results,
                         grid_s &region,
                         grid_s &grid,
                         grid_s &regional_grid,
                         long long &i){
  /**********************************************************************
  Purpose: This function copies results from global format to regional
          format for region i.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          long long i - Counter that holds current region index.
                        Runs from 0 to region.lat.size()-1.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          None - does copy results to regional_results, though.
  **********************************************************************/
  long long j,k;//Counters.

  //The range of the first index is the number of parameters, which
  //should be the same as results.
  regional_results.outputs.clear();
  regional_results.outputs.resize(results.outputs.size());

  //The ranges of the second indices are all equal to the # gr pts in rg
  for(k=0;k<(long long)results.outputs.size();k++){
    regional_results.outputs.at(k).resize(region.all_grid_indices.at(i).size(),0.0);
  }

  //Copy titles, units and omegas verbatim.
  regional_results.titles = results.titles;
  regional_results.units = results.units;
  regional_results.omegas = results.omegas;

  //Copy entire options structure.
  regional_results.options = results.options;

  //Copy values from results to regional_results if their grid
  //point is listed in region.all_grid_indices.at(i).
  for(j=0;j<(long long)region.all_grid_indices.at(i).size();j++){
    //Check to see that lat/lon in regional_grid matches grid's values.
    if(grid.lat.at(region.all_grid_indices.at(i).at(j)) != regional_grid.lat.at(j)) cout<<"grid.lat["<<region.all_grid_indices.at(i).at(j)<<"] = "<<grid.lat.at(region.all_grid_indices.at(i).at(j))<<" but regional_grid.lat["<<j<<"] = "<< regional_grid.lat.at(j)<<endl;
    if(grid.lon.at(region.all_grid_indices.at(i).at(j)) != regional_grid.lon.at(j)) cout<<"grid.lon["<<region.all_grid_indices.at(i).at(j)<<"] = "<<grid.lon.at(region.all_grid_indices.at(i).at(j))<<" but regional_grid.lon["<<j<<"] = "<< regional_grid.lon.at(j)<<endl;
    //Now loop through parameters (periods count twice!) to pop. reg_results
    for(k=0;k<(long long)regional_results.outputs.size();k++){
      regional_results.outputs.at(k).at(j) = results.outputs.at(k).at(region.all_grid_indices.at(i).at(j));
    }
  }
}

void display_output(results_s &results, grid_s &grid, long long number){
  /**********************************************************************
  Purpose: This function writes the results on the screen.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          long long number - # of grid pts to display for each parameter
                             Or, for 2D plots, num of pts in each line.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k, orig_num = number;//Number needs to be recalculated for 2D plots.

  switch(results.options.output_choice){
    default: cout<<"!!!!WARNING!!!!!! results.options.output_choice "<<results.options.output_choice<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{//Unstructured map output format.

      //Make sure requested number isn't too large.
      if(number > (long long)grid.lat.size()) number = (long long)grid.lat.size();

      for(j=0;j<(long long)results.outputs.size();j++){
        cout<<"Main Title: "<<results.titles.at(j)<<endl;
        cout<<"Phase format (see current_phase_format in definitions.hpp): "<<results.options.current_phase_format<<endl;
        cout<<"Units: "<<results.units.at(j)<<endl;
        cout<<MAP_HEADER_UNSTRUCTURED;
        for(i=0;i<number;i++){
          printf("%13.4f%14.4f%20.8e%15.4f",grid.lat.at(i),grid.lon.at(i),results.outputs.at(j).at(i),grid.elev.at(i));
          cout<<endl;//write buffer to screen.
        }
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 2:{//2D plot output format.

      for(j=0;j<(long long)results.xy.titles.size();j++){
        printf("Main Title: ");
        printf("%s\n",results.xy.titles.at(j).c_str());
        printf("X Axis Units: ");
        printf("%s\n",results.xy.x_units.at(j).c_str());
        printf("Y Axis Units: ");
        printf("%s\n",results.xy.y_units.at(j).c_str());

        printf("Number of dataseries, followed by the dataseries:\n");
        printf("%lld\n",(long long)results.xy.x_values.at(j).size());
        for(k=0;k<(long long)results.xy.x_values.at(j).size();k++){
          //Make sure requested number isn't too large.
          number = orig_num;
          if(number > (long long)results.xy.x_values.at(j).at(k).size()) number = (long long)results.xy.x_values.at(j).at(k).size();

          printf("****************************New data series:\n");

          //Only record legend if this plot has one for all lines.
          if((long long)results.xy.legends.size() > j){
            if(results.xy.legends.at(j).size() == results.xy.x_values.at(j).size()){
              printf("Legend for this dataseries:\n");
              printf("%s\n",results.xy.legends.at(j).at(k).c_str());
            }
          }

          printf("X,Y pairs (after # of points then the header line):\n");
          printf("%lld\n",(long long)results.xy.x_values.at(j).at(k).size());
          printf("%20s%20s\n","X value","Y value");
          for(i=0;i<number;i++){
            printf("%20.8e%20.8e\n",results.xy.x_values.at(j).at(k).at(i), results.xy.y_values.at(j).at(k).at(i));
          }//End of loop through points for this data series.
        }//End of loop through data series.
      }//End of loop through output plots.
      break;
    }
  }//End of switch-case.
}

void copy_output_to_latlon(results_s &results, grid_s &grid){
  /**********************************************************************
  Purpose: This function copies results of perform_analysis that have
          been saved as unstructured grid values to latlon grid values.
          It uses is_pt_in_bin, then checks for non/double/more-counted
          latlon pts, then uses grid_index_via_distance.
          Relatively fast, but doesn't use support pts and will look bad
          if the grid has been rotated.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k,l,doublecounted=0,notcounted=0;//Counters.
  long long index;
  vector< vector<long long> > numwrites;
  vector<double> averages;

  //Initialize latlon output.
  results.latlon.outputs.clear();
  results.latlon.outputs.resize(results.outputs.size());
  for(l=0;l<(long long)results.latlon.outputs.size();l++){
    results.latlon.outputs.at(l).resize(results.latlon.lat.size());
    for(j=0;j<(long long)results.latlon.lat.size();j++){
      results.latlon.outputs.at(l).at(j).resize(results.latlon.lon.size());
    }
  }
  //Initialize latlon elevations, which are derived from the unstructured grid.
  results.latlon.elev.clear();
  results.latlon.elev.resize(results.latlon.lat.size());
  for(j=0;j<(long long)results.latlon.lat.size();j++){
    results.latlon.elev.at(j).resize(results.latlon.lon.size());
  }

  //Create multivector to check that each latlon pt is matched to 1 orig grid pt.
  numwrites.resize(results.latlon.lat.size());
  for(j=0;j<(long long)results.latlon.lat.size();j++){
    numwrites.at(j).resize(results.latlon.lon.size(),0);
  }

  //Loop through original unstructured grid. 
  for(k=0;k<(long long)grid.lat.size();k++){
    //Just so bin boundaries are defined.
    int newbin=1; is_pt_in_bin(grid,90.0,180.0,k,newbin);
    //Loop through latlon latitudes.
    for(j=0;j<(long long)results.latlon.lat.size();j++){
      //Don't bother continuing unless this lat is in the bin's lat limits.
      if(results.latlon.lat.at(j)<grid.maxlat and results.latlon.lat.at(j)>=grid.minlat){
        //Loop through latlon longitudes.
        for(i=0;i<(long long)results.latlon.lon.size();i++){
          if(is_pt_in_bin(grid,results.latlon.lat.at(j),results.latlon.lon.at(i),k,newbin)){
            numwrites.at(j).at(i)++;
            results.latlon.elev.at(j).at(i) = grid.elev.at(k);//Crude, but fast.
            //Loop through output parameters.
            for(l=0;l<(long long)results.latlon.outputs.size();l++){
              results.latlon.outputs.at(l).at(j).at(i) = results.outputs.at(l).at(k);
            }//end of loop through parameters.
          }//if this longitude is in the unstructured grid bin.
        }//end of loop through latlon longitudes.
      }//if this latitude is in the unstructured grid bin.
    }//end of loop through latlon latitudes.
  }//end of loop through unstructured grid.

  //Now that the "easy" latlon points have been binned, time to catch the
  //few latlon points that fall on the line between bins, so they're not
  //caught by "is_pt_in_bin".  For these, use spherical_dist().
  //Loop through latlon latitudes.
  for(j=0;j<(long long)results.latlon.lat.size();j++){
    //Loop through FES longitudes.
    for(i=0;i<(long long)results.latlon.lon.size();i++){
      if(numwrites.at(j).at(i) != 1){
        index = grid_index_via_distance(grid, results.latlon.lat.at(j), results.latlon.lon.at(i), 0);
        numwrites.at(j).at(i) = 1;
        if(numwrites.at(j).at(i) == 0) notcounted++;
        else doublecounted++;
        results.latlon.elev.at(j).at(i) = grid.elev.at(index);//Crude, but fast.
        //Loop through output parameters.
        for(l=0;l<(long long)results.latlon.outputs.size();l++){
          results.latlon.outputs.at(l).at(j).at(i) = results.outputs.at(l).at(index);
        }//end of loop through parameters.
      }//if this latlon was notcounted or doublecounted.
    }//end of loop through latlon longitudes.
  }//end of loop through latlon latitudes.
  if(notcounted != 0)    cout<<"Number of (originally) non-counted latlon points             : "<<notcounted<<endl;
  if(doublecounted != 0) cout<<"Number of (originally) double (or more)-counted latlon points: "<<doublecounted<<endl;

  //Force NP/SP (if in latlon grid) to have averaged values that don't depend on longitude.
  for(j=0;j<(long long)results.latlon.lat.size();j++){
    if(fabs(results.latlon.lat.at(j))>89.9){//Anything within 10km of the poles should be identical.
      averages.clear();
      averages.resize(results.latlon.outputs.size(),0.0);
      //Determine avg values for all params.
      for(i=0;i<(long long)results.latlon.lon.size();i++){
        for(l=0;l<(long long)results.latlon.outputs.size();l++){
          averages.at(l) += results.latlon.outputs.at(l).at(j).at(i)/((double)results.latlon.lon.size());
        }
      }
      //Write avg values for all params to all longitudes.
      for(i=0;i<(long long)results.latlon.lon.size();i++){
        for(l=0;l<(long long)results.latlon.outputs.size();l++){
          results.latlon.outputs.at(l).at(j).at(i) = averages.at(l);
        }
      }
    }
  }
}

void copy_results_to_latlon(results_s &results,
                            grid_s &grid,
                            plot_options_s &plot_options){
  /**********************************************************************
  Purpose: This function generates a latlon grid that matches the latitude
          spacing of the unstructured grid, and matches the longitude
          spacing of the unstructured grid at its densest point.
          It then copies results of perform_analysis that have
          been saved as unstructured grid values to latlon grid values,
          and writes them to a folder.
          This function is different from copy_output_to_latlon because
          this function creates the grid and the folder and files, etc.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  grid_s llgrid;
  ancillary_s temp_ancillary;
  results_s temp_results;
  plot_options_s latlon_plot_options;
  double min_lon_spacing, min_lat_spacing;
  long long index_for_min_lon_spacing, index_for_min_lat_spacing;
  string base;

  if(grid.options.latlon == 0 or grid.options.type == 0) return;
  
  //Create latlon grid with "sufficient" density to represent unstructured grid.
  llgrid.options = grid.options;
  llgrid.options.type = 1;//equal-grid grid.
  llgrid.options.current_latlon = grid.options.latlon;//latlon format.
  //Unless proven otherwise, assume first point has smallest spacings.
  min_lon_spacing = grid.lon_halfwidth.front(); min_lat_spacing = grid.lat_halfwidth.front();
  index_for_min_lon_spacing=0; index_for_min_lat_spacing=0;
  //Skip first point.
  for(i=1;i<(long long)grid.lat.size();i++){
    if(grid.lon_halfwidth.at(i)<min_lon_spacing){
      min_lon_spacing=grid.lon_halfwidth.at(i);
      index_for_min_lon_spacing=i;
    }
    if(grid.lat_halfwidth.at(i)<min_lat_spacing){
      min_lat_spacing=grid.lat_halfwidth.at(i);
      index_for_min_lat_spacing=i;
    }
  }//End of loop through main grid.

  llgrid.options.lon_spacing = min_lon_spacing*2*deg2km*grid.options.latlon_lon_multiplier;
  llgrid.options.lat_spacing = min_lat_spacing*2*deg2km*grid.options.latlon_lat_multiplier;

  cout<<"Creating latlon grid with lat/lon spacings: "<<llgrid.options.lat_spacing*km2deg<<"/"<<llgrid.options.lon_spacing*km2deg<<" degrees."<<endl;
  define_grid(llgrid,temp_ancillary,temp_results);

  results.latlon = llgrid.latlon;

  copy_output_to_latlon(results,grid);

  //0 - Unstructured grid output in main folder, no latlon grid output at all.
  //1 - Unstructured grid output in main folder, latlon in "structured_output" subfolder.
  //2 - Latlon in main folder, unstructured grid output in "unstructured_output" subfolder.
  //3 - Latlon in main folder, no unstructured grid output at all.
  //LEAVE ROOM FOR LATLON BASED ON SUPPORT GRID DENSITY!
  plot_options.subfolder = plot_options.outputfolder;
  latlon_plot_options = plot_options;
  //Temporarily change output_choice so that latlon output can be written.
  int orig_output_choice = results.options.output_choice;
  results.options.output_choice = 5;//latlon
  base = "latlon_data";
  if(grid.options.latlon == 1){
    plot_options.subfolder.append("structured_output/");
    create_folder(plot_options.subfolder);
    latlon_plot_options.outputfolder = plot_options.subfolder;
  }
  else if(grid.options.latlon == 2){
    plot_options.subfolder.append("unstructured_output/");
    create_folder(plot_options.subfolder);
    plot_options.outputfolder = plot_options.subfolder;
  }
  else if(grid.options.latlon == 3){
  }
  else cout<<"WARNING!! grid.options.latlon "<<grid.options.latlon<<" wasn't recognized!"<<endl;
  write_output(base,results, grid, latlon_plot_options,1);

  //Now change output_choice back to its original value.
  results.options.output_choice = orig_output_choice;
}

void write_output(string base,
                  results_s &results,
                  grid_s &grid,
                  plot_options_s &plot_options,
                  int final){
  /**********************************************************************
  Purpose: This function takes the results of perform_analysis() and
          uses it to create output text files that are read by
          create_plots.pro to create maps/plots.

          It accepts a string as its 1st argument that serves as a "base"
          for the filenames that it saves in plot_options.outputfolder.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)

          string base - String which forms the base of new filenames.  For
                        example, if "plot_data" is the base, the 1st
                        filename would be "plot_data_01.txt".

          int final - 0 if this isn't the last call, 1 if final write.
  ***********************************************************************
  Output:
            Files are created which can be read by the IDL program
            create_plots.pro
  **********************************************************************/
  long long i,j,k,l,numfiles;//counters.
  FILE *new_fp;
  string temp;

  switch(results.options.output_choice){
    default: cout<<"!!!!WARNING!!!!!! results.options.output_choice "<<results.options.output_choice<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{//Unstructured map output format.

      numfiles = results.outputs.size();
      init_filenames(base, numfiles, plot_options);
      //New output format has every variable preceded by header line so
      //create_plots can be written flexibly.
      if(numfiles>0) for(j=0;j<numfiles;j++){
        new_fp = fopen(plot_options.output_files.at(j).c_str(),"w");
        write_map_header(results,j,new_fp);
        for(i=0;i<(long long)grid.lat.size();i++){
          fprintf(new_fp,"%13.4f%14.4f%20.8e%9.2f\n",grid.lat.at(i),grid.lon.at(i),results.outputs.at(j).at(i),grid.elev.at(i));
        }
        fclose(new_fp);
      }
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 2:{//2D plot output format.

      numfiles = results.xy.titles.size();
      init_filenames(base, numfiles, plot_options);
      //New output format has every variable preceded by header line so
      //create_plots.pro can be written more flexibly.
      if(numfiles>0) for(j=0;j<numfiles;j++){
        new_fp = fopen(plot_options.output_files.at(j).c_str(),"w");
        fprintf(new_fp,"Main Title:\n");
        fprintf(new_fp,"%s\n",results.xy.titles.at(j).c_str());
        fprintf(new_fp,"X Axis Units:\n");
        fprintf(new_fp,"%s\n",results.xy.x_units.at(j).c_str());
        fprintf(new_fp,"Y Axis Units:\n");
        fprintf(new_fp,"%s\n",results.xy.y_units.at(j).c_str());

        //By default IDL sets the bounds to the max/min of all series.
        //But if these lines are written later I can insert some statements
        //to override the bounds.
        /*fprintf(new_fp,"X upper bound:\n");
        fprintf(new_fp,"X lower bound:\n");
        fprintf(new_fp,"Y upper bound:\n");
        fprintf(new_fp,"Y lower bound:\n");
        //*/

        fprintf(new_fp,"Number of dataseries, followed by the dataseries:\n");
        fprintf(new_fp,"%lld\n",(long long)results.xy.x_values.at(j).size());
        for(k=0;k<(long long)results.xy.x_values.at(j).size();k++){
          fprintf(new_fp,"****************************New data series:\n");

          //Only record legend if this plot has one for all lines.
          if((long long)results.xy.legends.size() > j){
            if(results.xy.legends.at(j).size() == results.xy.x_values.at(j).size()){
              fprintf(new_fp,"Legend for this dataseries:\n");
              fprintf(new_fp,"%s\n",results.xy.legends.at(j).at(k).c_str());
            }
            else cout<<"WARNING!!!! results.xy.legends.at(j).size() = "<<results.xy.legends.at(j).size()<<", results.xy.x_values.at(j).size() = "<<results.xy.x_values.at(j).size()<<endl; 
          }

          //fprintf(new_fp,"Plotting type (0-pts,1-straight lines,2-straight lines+pts,3-dashes,4-boxes,5-Crosses,etc):\n");

          fprintf(new_fp,"X,Y pairs (after # of points then the header line):\n");
          fprintf(new_fp,"%lld\n",(long long)results.xy.x_values.at(j).at(k).size());
          fprintf(new_fp,"%20s%20s\n","X value","Y value");
          for(i=0;i<(long long)results.xy.x_values.at(j).at(k).size();i++){
            fprintf(new_fp,"%20.8e%20.8e\n",results.xy.x_values.at(j).at(k).at(i), results.xy.y_values.at(j).at(k).at(i));
          }//End of loop through points for this data series.
        }//End of loop through data series.
        fclose(new_fp);
      }//End of loop through output files.
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 3:{//Maps of separated points- no contours as in types 0,1.

      numfiles = results.outputs.size();
      init_filenames(base, numfiles, plot_options);
      //New output format has every variable preceded by header line so
      //create_plots can be written flexibly.
      if(numfiles>0) for(j=0;j<numfiles;j++){
        new_fp = fopen(plot_options.output_files.at(j).c_str(),"w");
        fprintf(new_fp,"Main Title:\n");
        fprintf(new_fp,"%s\n",results.titles.at(j).c_str());
        fprintf(new_fp,"Map data (after header line):\n");
        fprintf(new_fp,"     Latitude     Longitude      Type\n");
        for(i=0;i<(long long)grid.lat.size();i++){
          fprintf(new_fp,"%13.4f%14.4f%10lld\n",grid.lat.at(i),grid.lon.at(i),(long long)results.outputs.at(j).at(i));
        }
        fclose(new_fp);
      }
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 4:{//Maps of separated points with labels.

      numfiles = results.titles.size();
      init_filenames(base, numfiles, plot_options);
      //New output format has every variable preceded by header line so
      //create_plots can be written flexibly.
      if(numfiles>0) for(j=0;j<numfiles;j++){
        new_fp = fopen(plot_options.output_files.at(j).c_str(),"w");
        fprintf(new_fp,"Main Title:\n");
        fprintf(new_fp,"%s\n",results.titles.at(j).c_str());
        fprintf(new_fp,"Map data (after header line):\n");
        fprintf(new_fp,"     Latitude     Longitude      Label\n");
        for(i=0;i<(long long)grid.lat.size();i++){
          fprintf(new_fp,"%13.4f%14.4f      %s\n",grid.lat.at(i),grid.lon.at(i),results.labels.at(j).at(i).c_str());
        }
        fclose(new_fp);
      }
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 5:{//Map using 2D latlon equal-grid format.

      numfiles = results.latlon.outputs.size();
      init_filenames(base, numfiles, plot_options);
      //New output format has every variable preceded by header line so
      //create_plots can be written flexibly.
      if(numfiles>0) for(l=0;l<numfiles;l++){
        new_fp = fopen(plot_options.output_files.at(l).c_str(),"w");
        write_map_header(results,l,new_fp);
        for(j=0;j<(long long)results.latlon.lat.size();j++){
          for(i=0;i<(long long)results.latlon.lon.size();i++){
            fprintf(new_fp,"%9.2f%16.8e\n",results.latlon.elev.at(j).at(i),results.latlon.outputs.at(l).at(j).at(i));
          }
        }
        fclose(new_fp);
      }
      break;
    }
  }//End of switch-case.
  //Now save a file that IDL can use to easily read in the filenames.
  //(Only do this if this is the "final" call to write_univsal_output!)
  if(final == 1 and numfiles > 0) create_output_filenames(plot_options,results);
}

void init_filenames(string base,
                    long long numfiles,
                    plot_options_s &plot_options){
  /**********************************************************************
  Purpose: This function initializes filename vectors to be used in
          write_output. It accepts a string as its 1st argument that 
          serves as a "base" for the filenames, and the # of files.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)

          string base - String which forms the base of new filenames.  For
                        example, if "plot_data" is the base, the 1st
                        filename would be "plot_data_01.txt".

          longint numfiles - Number of filenames to fill vector with.
  ***********************************************************************
  Output:  (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;//counters.
  char s[max_length];
  plot_options.output_files.clear();
  plot_options.output_files.resize(numfiles);
  plot_options.just_the_filenames.clear();
  plot_options.just_the_filenames.resize(numfiles);
  for(i=0;i<numfiles;i++){
    plot_options.output_files.at(i) = plot_options.outputfolder;
    plot_options.output_files.at(i).append(base);
    sprintf(s,"_%04lld.txt",i+1);
    plot_options.output_files.at(i).append(s);
    plot_options.just_the_filenames.at(i) = base;
    plot_options.just_the_filenames.at(i).append(s);
  }
}

void write_map_header(results_s &results, long long j, FILE *new_fp){
  /**********************************************************************
  Purpose: This function writes the header for map files.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          j - index of parameter being written to disk
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  fprintf(new_fp,"Main Title:\n");
  fprintf(new_fp,"%s\n",results.titles.at(j).c_str());
  fprintf(new_fp,"Units:\n");
  fprintf(new_fp,"%s\n",results.units.at(j).c_str());
  //Record phase format in short form.
  fprintf(new_fp,"Phase format (see current_phase_format in definitions.hpp):\n");
  fprintf(new_fp,"%d\n",results.options.current_phase_format);

  //Only bother if error bars are right length, and this one is > 0.0.
  if(results.outputs.size() == results.error_bars.size() and results.outputs.size() > 0){
    if(results.error_bars.at(j) > 0.0){
      fprintf(new_fp,"Error bar:\n");
      fprintf(new_fp,"%20.8e\n",results.error_bars.at(j));
    }
  }
  //Can't change this line without screwing up header_lines.pro
  switch(results.options.output_choice){
    default:{
      fprintf(new_fp,"Map data (after header line):\n");
      fprintf(new_fp,"%13s%14s%20s%9s\n","Latitude","Longitude","Output","Elev(m)");
      break;
    }
    case 5:{
      fprintf(new_fp,"Number of latitudes, then 1st and last latitudes:\n");
      fprintf(new_fp,"%lld\n",(long long)results.latlon.lat.size());
      fprintf(new_fp,"%13.4f\n",results.latlon.lat.front());
      fprintf(new_fp,"%13.4f\n",results.latlon.lat.back());
      fprintf(new_fp,"Number of longitudes, then 1st and last longitudes:\n");
      fprintf(new_fp,"%lld\n",(long long)results.latlon.lon.size());
      fprintf(new_fp,"%14.4f\n",results.latlon.lon.front());
      fprintf(new_fp,"%14.4f\n",results.latlon.lon.back());
      fprintf(new_fp,"Mask value (not physical, shouldn't be converted to/from sin/cos):\n");
      fprintf(new_fp,"%14.4f\n",results.latlon.mask);
      fprintf(new_fp,"Map data (after header line):\n");
      fprintf(new_fp,"%9s  %s\n","Elev(m)","Output- values at all lons at 1st lat, then all lons at 2nd lat...");
      break;
    }
  }
}

void create_output_filenames(plot_options_s &plot_options,
                             results_s &results){
  /**********************************************************************
  Purpose: This function creates the universal file index called
          output_filenames.txt.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          A file-- output_filenames.txt-- is created which contains the
          filenames of all the output files that will be read by the IDL
          program create_plots.pro.
  **********************************************************************/
  long long j;
  FILE *new_fp;
  string temp;

  temp = plot_options.outputfolder;
  temp.append("output_filenames.txt");
  new_fp = fopen(temp.c_str(),"w");
  fprintf(new_fp,"Output choice (see definitions.hpp for details):\n");
  fprintf(new_fp,"%d\n",results.options.output_choice);

  fprintf(new_fp,"Filenames (number of filenames listed first):\n");
  fprintf(new_fp,"%lld\n",(long long)plot_options.just_the_filenames.size());
  for(j=0;j<(long long)plot_options.just_the_filenames.size();j++){
    fprintf(new_fp,"%s\n",plot_options.just_the_filenames.at(j).c_str());
  }
  fclose(new_fp);
}

void find_and_load_parallel_region_output(
              grid_s &region,
              plot_options_s &plot_options,
              results_s &results,
              grid_s &grid){
  /**********************************************************************
  Purpose: This function searches plot_options.outputfolder for the output
          of each region's analysis when region_options.parallel > 0.

          The num of parameters is not assumed, but the num of regions
          found is compared to region.lat.size().  Also, when loading
          points from a region, the function insures that any nonzero pts
          outside of region.all_grid_indices.at(i) give a warning.

          The filenames that contain "region_" ALL are returned in
          plot_options.  A routine later in main() will use this list to
          delete these temporary files.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          Modifies structure results so that it contains output
          from all the files created by the parallel region code.
          Also, all these filenames are recorded in plot_options.
  **********************************************************************/
  //These variables are used to search the output folder.
  DIR *outputDIR;
  struct dirent *outputDirEnt;
  string temp;
  char s[max_length];
  long long i,j;
  results_s temp_results;
  //When each filename is read, the region index and parameter index
  //are extracted from the filename (remember the +1!) and stored here.
  long long current_region,current_parameter;

  //This vector holds region/parameter indices for each file.
  vector< vector<long long> > file_info;
  vector<long long> temp_vector_long;//Used to push_back vector of vecs

  //Used to make sure that region.indices_to_analyze has every region
  //accounted for in the region output files.  Each entry starts as 0 and
  //is incremented when ANY parameter's region file is identified.
  vector<long long> parameters_found_for_region;
  parameters_found_for_region.resize(region.indices_to_analyze.size(),0);

  //Used to search regions.indices_to_analyze for each temp file's region
  //index so that parameters_found_for_region can be incremented each time
  //a new parameter for that region is found.
  vector<long long>::iterator irita;
  long long ritai;//irita changed to an index.

  period_info_s period_info;

  //Clear the results vectors just in case they're populated.
  results.outputs.clear();
  results.titles.clear();
  results.units.clear();
  //Record this original value.
  long long orig_output_size = (long long)results.options.output_type.size();
  //These may have changed in perform_analysis, but param vectors probably haven't.
  results.options.output_type.clear();
  results.options.output_tide.clear();

  //Open the output folder.
  outputDIR = opendir(plot_options.outputfolder.c_str());

  //Search the output folder for files with names such as
  //"region_*" - these are the files created during parallel processing.
  outputDirEnt = readdir(outputDIR);
  i=0;
  while(outputDirEnt != NULL){
    temp = plot_options.outputfolder;
    strcpy(s,outputDirEnt->d_name);
    temp.append(s);
    //If this filename contains "region_", it needs to be read.
    //BUT- it shouldn't be the region_overlap_plots folder!
    if(((int)temp.find("region_"))!=(int)string::npos and ((int)temp.find("region_overlap_plots"))==(int)string::npos){
      plot_options.output_files.push_back(temp);
      //cout<<plot_options.output_files.at(i)<<endl;
      file_info.push_back(temp_vector_long);

      //Read the region index and the parameter index from the filename,
      //store in file_info vector.  Also look for max value of parameter
      //and region to make sure number of regions is right and to resize
      //the results vectors using the number of parameters.
      sscanf(s,"region_%5lld_plot_data_%4lld.txt", &current_region,&current_parameter);
      current_region--; current_parameter--;//Filenames are +1!
      file_info.at(i).push_back(current_region);
      file_info.at(i).push_back(current_parameter);
      //Search region.indices_to_analyze for current_region.
      ritai = -1;
      irita = find(region.indices_to_analyze.begin(),region.indices_to_analyze.end(),current_region);
      if(irita != region.indices_to_analyze.end()){
        ritai = (long long)(irita - region.indices_to_analyze.begin());
        parameters_found_for_region.at(ritai)++;
      }
      else{
        cout<<"!!WARNING!! Loading parallel output from disk, but file "<<temp<<" belongs to region with index "<<current_region<<" which isn't in region.indices_to_analyze:"<<endl;
        for(j=0;j<(long long)region.indices_to_analyze.size();j++){
          cout<<" "<<region.indices_to_analyze.at(j);
          if(j<(long long)region.indices_to_analyze.size()-1)
            cout<<",";
          else
            cout<<"."<<endl;
        }
      }
      i++;
    }//End of "does this filename contain "region_" " statement.
    outputDirEnt = readdir(outputDIR);
  }//End of while loop through files in outputfolder.

  //Close the main output folder.
  closedir(outputDIR);

  //Check to make sure that the right number of parameters were found for
  //each region.
  for(i=0;i<(long long)parameters_found_for_region.size();i++){
    if(parameters_found_for_region.at(i) != orig_output_size and results.options.type != -1) cout<<"!!WARNING!! Loading parallel output from disk, but parameters_found_for_region["<<i<<"] (corresponding to region.indices_to_analyze["<<i<<"] = "<<region.indices_to_analyze.at(i)<<") was "<<parameters_found_for_region.at(i)<<", but there should be "<<orig_output_size<<" output parameters for each region."<<endl;
  }

  //Use num_parameters to resize titles, units, outputs.
  results.outputs.resize(orig_output_size);
  results.titles.resize(orig_output_size);
  results.units.resize(orig_output_size);
  //Use grid.lat.size() to resize outputs (all of them).
  for(i=0;i<(long long)results.outputs.size();i++){
    results.outputs.at(i).resize(grid.lat.size());
  }

  //debug
  if(1==2) for(i=0;i<(long long)plot_options.output_files.size();i++){
    cout<<"File "<<plot_options.output_files.at(i)<<" is from region "<<file_info.at(i).front()+1<<endl;
    cout<<"Center grid indices in this region are: "<<endl;
    for(j=0;j<(long long)region.center_grid_indices.at(file_info.at(i).front()).size();j++){
      cout<<region.center_grid_indices.at(file_info.at(i).front()).at(j)<<endl;
    }
    cout<<endl;
  }

  //Now read the files one by one, saving only the points that are in the
  //region according to region.center_grid_indices.at(i)
  for(i=0;i<(long long)plot_options.output_files.size();i++){
    //Load the titles, units and output from this output file.
    //All titles, units and output are in index 0 b/c just a single file
    //Be careful to load the same type of output file as was just saved.
    //Next statement controls legacy map, unstructured map, 2D plot loading.
    temp_results.options.output_choice = results.options.output_choice;
    read_output(temp_results, plot_options.output_files.at(i), grid);
    if(temp_results.options.output_choice > 2) cout<<"!!WARNING!! results.options.output_choice not recognized: "<<results.options.output_choice<<endl;

    //Copy the titles, units, phase format to main results struct.
    results.options.current_phase_format=temp_results.options.current_phase_format;
    results.titles.at(file_info.at(i).at(1)) = temp_results.titles.front();
    results.units.at(file_info.at(i).at(1)) = temp_results.units.front();

    //Use titles to build output type,tide vectors.
    convert_title_to_parameter(temp_results.titles.front(),period_info);
    results.options.output_type.push_back(period_info.output_type);
    results.options.output_tide.push_back(period_info.tide);

    //Loop through grid points to copy output.
    for(j=0;j<(long long)temp_results.outputs.front().size();j++){
      if(count(region.center_grid_indices.at(file_info.at(i).front()).begin(),region.center_grid_indices.at(file_info.at(i).front()).end(),(long long)j) != 0){
        //Copy data from temp_results to its correct vector in results.
        results.outputs.at(file_info.at(i).at(1)).at(j) = temp_results.outputs.front().at(j);
      }
      //Check that pts in this output file are non-zero only in its region.
      else if(fabs(temp_results.outputs.front().at(j)) > tolerance){
        cout<<"File "<<plot_options.output_files.at(i)<<" has value "<<fixed<<setprecision(9)<<temp_results.outputs.front().at(j)<<" but this file is from region "<<file_info.at(i).front()+1<<" and it shouldn't contain nonzero results from this grid point!"<<endl;
      }
    }
  }//End of loop through files.
}

void upward_continuation_verification(grid_s &grid,
                                      input_s &input,
                                      input_s &unused_input,
                                      results_s &results,
                                      grid_s &region,
                                      plot_options_s &plot_options){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  int disable_support=0; grid_s backup_grid = grid;
  long long i,j;
  results_s regional_results;
  grid_s regional_grid;
  string base;//This string forms the base part of the output filename.
  char s[max_length];

  //The following are used for fork() to enable parallel processing.
  pid_t pid;
  int rv;

  //Only bother with verification if it's requested.
  if(results.options.upward_continuation_verification_choice == 1){
    //Defines where temporary simulated input file will be saved.
    input.options.output_folder = "data/GRACE/simulations/";
    input.options.output_filename = "input_and_orbit_verification_temp.txt";

    //And only upward continue if that's requested.
    if(results.options.skip_upward_continuation_during_verification == 0){
    
      results_s ntresults,temp;//Used for separate non-tide soln.
      results_s regional_ntresults;//Used for separate non-tide soln.

      cout<<"Performing upward continuation verification with separate non-tide-only solution."<<endl;
      if(results.options.type == 0) cout<<"WARNING!!! Can't upcont type 0 maps, because non-tide soln needs to be computed separately. Don't know how to fix this because type 0 maps don't inherit the results type of whatever created them..."<<endl;
      if(region.options.type != 2) cout<<"WARNING!!! Can't upcont with anything except universal region!"<<endl;

      //Copy results to new ntresults, strip non-tide params.
      ntresults = results;
      
      //Because inside_load_options never runs.
      //THIS ONLY WORKS WITH *SOME* ANALYSIS TYPES!
      ntresults.options.current_phase_format = 3;
      ntresults.options.adjust_phases_choice = 3;
      
      ntresults.options.parameter.clear();
      ntresults.options.parameter_type.clear();
      for(j=0;j<(long long)results.options.parameter_tide.size();j++){
        if(results.options.parameter_tide.at(j)==0){
          ntresults.options.parameter.push_back(results.options.parameter.at(j));
          ntresults.options.parameter_type.push_back(results.options.parameter_type.at(j));
        }
      }
     
      //Search for non-tide subset of current params in cache (should be there.)
      least_squares_cache_initialize(grid, region, input, ntresults);

      //Match outputs in results to those in ntresults for easy indexing.
      vector<long long> ntmatch;
      long long ntcnt=0;
      for(j=0;j<(long long)results.options.output_tide.size();j++){
        if(results.options.output_tide.at(j)==0){
          ntmatch.push_back(ntcnt);
          ntcnt++;
        }
        else ntmatch.push_back(-1);
      }

      //Now create all the output vectors, including stuff normally done in define_grid.
      create_output_vectors(ntresults,input);
      //Now resize the second dimension (have to do this multiple times)
      //so that the second dimension holds all grid points.
      //Note that the first dimension was set in create_output_vectors().
      for(j=0;j<(long long)ntresults.options.output_type.size();j++){
        ntresults.outputs.at(j).resize(grid.lat.size(),0.0);
      }

      //Now actually solve for non-tide soln, should be an ls cache subset.
      ancillary_s temp_ancillary;
      model_s temp_model;
      plot_options_s temp_options;
      insitu_s temp_insitu;
      perform_analysis(input, grid, temp_ancillary, temp_model, ntresults, temp_options, region, temp_insitu);

      if(disable_support==1){
        grid.options.support_type=0;
        define_support_grid(grid);
        cout<<"Note: Supporting grid was cleared right before loop containing perform_upward_continuation2 to standardize comparisons between solutions created with different support multipliers (and no support grid at all) and to speed up comparisons."<<endl;
      }

      //Just checking.
      if(results.options.h_matrix_choice != 1 and grid.options.support_type != 0) cout<<"WARNING!!! upcontver can't work with support grid yet!!!"<<endl;

      //Zero all but one tide to examine that tide alone.
      if(1==2){
        for(j=0;j<(long long)results.options.output_tide.size();j++){
          if(results.options.output_tide.at(j)==1){
            //Look for string in title, zero if not found (i.e. npos).
            //Don't need to look for 'Alias' because output_tide==0 for it.
            if((int)results.titles.at(j).find("M2")==(int)string::npos){
              //Zero all amplitudes and phases that DON'T match the above string.
              cout<<"Zeroing "<<results.titles.at(j)<<endl;
              for(i=0;i<(long long)grid.lat.size();i++){
                results.outputs.at(j).at(i) = 0.0;
              }
            }
            else cout<<"Not zeroing "<<results.titles.at(j)<<endl;
          }
        }
      }

      //Newly computed non-tide soln needs to be put into same format as combined soln
      //so upward continuation doesn't need to loop through both multivectors separately. 
      temp=ntresults;
      ntresults=results;
      for(j=0;j<(long long)results.options.output_tide.size();j++){
        if(ntmatch.at(j)>=0){
          ntresults.outputs.at(j) = temp.outputs.at(ntmatch.at(j));
        }
      }

      results.options.adjust_phases_choice = 4;//Make rel. to input.epoch.
      adjust_phases(input, results);
      ntresults.options.adjust_phases_choice = 4;//Make rel. to input.epoch.
      adjust_phases(input, ntresults);

      //Needs to be 2 - don't have non-region code working...
      region.options.type = 2;

      //Needs to be 1 - don't have sequential code working...
      region.options.parallel = 1;

      //Over-write region with regions specifically chosen for upward cont.
      region.options.noload=0;//Don't bother displaying it.
      region = define_region(region.options, grid, 1);

      //Over-write input because original analysis may have been performed w/
      //no regions, but region code (i.e. binindices vector) is required here
      //because I haven't written upward continuation code without regions.
      load_input(input,grid,region);
      //Don't split input yet because upward continuation should use
      //all input points.  I'll run split_input after the upward_continuation.

      //I want these to both be 0.
      region.options.analysis_list_cache_only = 0;
      region.options.analysis_list_choice = 0;

      //To avoid collisions between simultaneous instances of GAIA, each instance
      //writes to a unique time-stamped output folder. This allows the user to
      //quickly revisit old upward continuation results, but also quickly fills
      //up with these huge input verification files!
      string prefix = current_datetime(); prefix.append("/");
      input.options.output_folder.append(prefix);
      string desired_foldername = gaiafolder();
      desired_foldername.append(input.options.output_folder);
      create_folder(desired_foldername);

      if(region.lat.size() == 0) cout<<"HAVEN'T WRITTEN NON-REGION CODE!"<<endl;
      else{
        //Regions are not analyzed in a direct order.  They are ordered
        //in different ways - region.options.analysis_list_choice controls
        //most of the variability.  For instance, regions with more in situ
        //stations can be analyzed first.
        build_analysis_region_list(grid, region);

        //Analyze regions either using sequential or parallel code:
        switch(region.options.parallel){
          default: cout<<"!!!!WARNING!!!!!! region.options.parallel "<<region.options.parallel<<" isn't recognized."<<endl;
          case 0:{//Sequential region code.
            cout<<"HAVEN'T WRITTEN THIS YET!"<<endl;
            break;
          }
          case 1:{//Parallel region code.
            //Loop through regions, analyze data in each region in a diffrnt
            //forked process, then writing the regional results to separate
            //files which will all be combined at the end.

            //Loop through regions, start a new process for each region.
            for(j=0;j<(long long)region.indices_to_analyze.size();j++){
              //Figure out which reg index should be analyzed on this loop.
              i=region.indices_to_analyze.at(j);

              switch(pid = fork()){
                default:{//This code runs in the parent process.
                  //If the number of region processes already started is
                  //greater to or equal to the numprocesses, wait until one
                  //of the processes finishes before starting another.
                  if(j+1 >= results.options.numprocesses) wait(&rv);
                  break;
                }//End of parent process commands.
                case -1:{
                  perror("fork");//Something went wrong!
                  cout<<"Couldn't open fork!"<<endl;
                  exit(1);//Parent exits.
                  break;
                }
                case 0:{//This code is run in child process that fork creates
                  results.options.region_index = i;
                  define_regional_grid(grid,region,regional_grid,i);

                  copy_results_from_global_to_regional(regional_results,results,region,grid,regional_grid,i);
                  copy_results_from_global_to_regional(regional_ntresults,ntresults,region,grid,regional_grid,i);

                  if(region.all_grid_indices.at(i).size() > 0){
                    region.input = load_regional_input((int)i,input,region);
                  }
                  else cout<<results.options.prefix<<": Skipping loading of input data."<<endl;

                  if(region.all_grid_indices.at(i).size() > 0){
                    cout<<results.options.prefix<<": Performing upward continuation."<<endl;
                    perform_upward_continuation2(region.input, regional_grid, regional_results, regional_ntresults,-1);//-1 disables accelmap because it's an invalid index.
                  }
                  else cout<<"Skipping upward continuation in region "<<i+1<<" of "<<(long long)region.indices_to_analyze.size()<<endl;

                  //Write results from this region's process to disk so that
                  //the parent can compile all regional results together
                  //once all the processes are done.
                  base = "region"; sprintf(s,"_%05lld_input.txt",i+1);
                  base.append(s);
                  save_regional_verification_input(base, region.input);

                  exit(i);//Exit value of child process is the region index.
                  break;
                }//End of child process commands.
              }//End of switch-case-break for "parent or child".
            }//End of loop through regions.

            //Wait for still-running procs (should be "numprocesses" of em..)
            //But if fewer regions are to be analyzed than numprocesses,
            //only wait for the regions that are really being analyzed.
            for(i=0;i<min((long long)results.options.numprocesses,(long long)region.indices_to_analyze.size());i++) wait(&rv);

            //Each of these processes has written its data to disk.  Search for
            //these files and load them sequentially, writing the input data to
            //a new file that is appended with each new file.  Delete each
            //region's input file after loading.
            combine_regional_verification_inputs(input, region);

            break;
          }
        }//End of parallel processing switch-case-break.

      }//End of region.type != 0 else statement.

      cout<<"Upward continuation finished."<<endl;
    }
    else{
      input.options.output_folder.append(results.options.folder_for_skipping);
      cout<<"Skipping upward continuation, loading verif folder "<<input.options.output_folder<<endl;
    }

    //Now it's time to split the input, so that comparing the two input
    //files will result in some input points counting as inversion points
    //and other input points counting as verification_points.  Shouldn't
    //be done before now! Also, note that regions are used so
    //unused_input is empty- this just creates the verification_points vector.
    //WARNING: UNUSED_INPUT IS PASSED TO THIS FUNCTION SO IT MAY BE FULL
    //IF REGIONS WEREN'T USED IN GAIA!
    if(input.options.split_input!=0) unused_input=split_input(input,region);

    input.file_comparison_method = results.options.upward_continuation_verification_choice;
    compare_input_file_to_verification_file(input,grid,results);
    
    //***************************************************************************
    //Now calculate noise reduction for inversion accels,
    //using the separate non-tide soln and then the complete soln w/o land tides.
    results_s noise, temp_results;
    temp_results.options.type=1010;
    temp_results.options.cutoff_freq=0.01;
    input_s temp_input, verif_input, orig_input;
    //push_back vs of string vectors
    vector<string> temp_v_string;

    //Load original inversion accels.
    region.lat.clear();//Non-region loading is easier here.
    temp_input.options = input.options;//Foldernames, etc.
    load_input(temp_input,grid,region);
    temp_input.verification_points = input.verification_points;//Best of both worlds.
    verif_input=split_input(temp_input,region);//Set aside for later.
    orig_input = temp_input;//Also set this aside for later.

    if(1==2){
      temp_input.options.output_folder = input.options.basefolder;
      temp_input.options.output_filename= "input_and_orbit_inversion.txt";
      cout<<"Writing inversion input data to file "<<temp_input.options.output_folder<<temp_input.options.output_filename<<endl;
      write_input(temp_input);
      cout<<"Finished writing inversion input to disk."<<endl;
      
      temp_input=verif_input;//Set aside for later.
      
      verif_input.options.output_folder = input.options.basefolder;
      verif_input.options.output_filename= "input_and_orbit_verification.txt";
      cout<<"Writing verification input data to file "<<verif_input.options.output_folder<<verif_input.options.output_filename<<endl;
      write_input(verif_input);
      cout<<"Finished writing verification input to disk."<<endl;
      
      verif_input=temp_input;//reset.
      temp_input=orig_input;//reset.
    }

    //Calculate power spectrum.
    perform_timeseries_analysis(temp_input, temp_results);
    temp_results.xy.titles.front().append(" (Inversion)");
    temp_results.xy.legends.push_back(temp_v_string);
    temp_results.xy.legends.front().push_back("Orig. accels");

    //Copy this spectrum to the final noise results structure.
    copy_plot_line(temp_results,noise,1,1,1);//Orig plot #, line #, new plot in noise.

    //Extract complete inversion accels from verif file (no land tides).
    //1:combined residual w/o land tides, 2:ocean tides, 3:non-tides sep. soln, 4:land tides.
    extract_signal(temp_input,0,1);//inver(0)/verif(1) accels, next # is signal type.

    //Subtract from original inversion accels.
    if(temp_input.accel.size() != orig_input.accel.size()) cout<<"WARNING!!! VERIFICATION ACCEL SIZE ("<<temp_input.accel.size()<<") ISN'T THE SAME AS ORIGINAL ACCEL SIZE ("<<orig_input.accel.size()<<")!"<<endl;
    for(i=0;i<(long long)temp_input.accel.size();i++) temp_input.accel.at(i) = orig_input.accel.at(i) - temp_input.accel.at(i);

    //Calculate power spectrum.
    perform_timeseries_analysis(temp_input, temp_results);
    temp_results.xy.titles.front().append(" (Inversion)");
    temp_results.xy.legends.push_back(temp_v_string);
    temp_results.xy.legends.front().push_back("ocean + nontides");

    //Copy this spectrum to the final noise results structure.
    copy_plot_line(temp_results,noise,1,1,0);//Orig plot #, line #, new plot in noise.

    //Extract separate non-tide inversion accels from verif file.
    extract_signal(temp_input,0,3);//inver(0)/verif(1) accels, next # is signal type.

    //Subtract from original inversion accels.
    if(temp_input.accel.size() != orig_input.accel.size()) cout<<"WARNING!!! VERIFICATION ACCEL SIZE ("<<temp_input.accel.size()<<") ISN'T THE SAME AS ORIGINAL ACCEL SIZE ("<<orig_input.accel.size()<<")!"<<endl;
    for(i=0;i<(long long)temp_input.accel.size();i++) temp_input.accel.at(i) = orig_input.accel.at(i) - temp_input.accel.at(i);

    //Calculate power spectrum.
    perform_timeseries_analysis(temp_input, temp_results);
    temp_results.xy.titles.front().append(" (Inversion)");
    temp_results.xy.legends.push_back(temp_v_string);
    temp_results.xy.legends.front().push_back("nontides");

    //Copy this spectrum to the final noise results structure.
    copy_plot_line(temp_results,noise,1,1,0);//Orig plot #, line #, new plot in noise.

    //***************************************************************************
    //Now calculate noise reduction for verification accels,
    //using the separate non-tide soln and then the complete soln w/o land tides.

    //Calculate power spectrum of original verification accels.
    perform_timeseries_analysis(verif_input, temp_results);
    temp_results.xy.titles.front().append(" (Verification)");
    temp_results.xy.legends.push_back(temp_v_string);
    temp_results.xy.legends.front().push_back("Orig. accels");

    //Copy this spectrum to the final noise results structure.
    copy_plot_line(temp_results,noise,1,1,1);//Orig plot #, line #, new plot in noise.

    //Extract complete inversion accels from verif file (no land tides).
    //1:combined residual w/o land tides, 2:ocean tides, 3:non-tides sep. soln, 4:land tides.
    extract_signal(temp_input,1,1);//inver(0)/verif(1) accels, next # is signal type.

    //Subtract from original verification accels.
    if(temp_input.accel.size() != verif_input.accel.size()) cout<<"WARNING!!! VERIFICATION ACCELS AREN'T THE SAME LENGTH AS ORIGINAL ACCELS!"<<endl;
    for(i=0;i<(long long)temp_input.accel.size();i++) temp_input.accel.at(i) = verif_input.accel.at(i) - temp_input.accel.at(i);

    //Calculate power spectrum.
    perform_timeseries_analysis(temp_input, temp_results);
    temp_results.xy.titles.front().append(" (Verification)");
    temp_results.xy.legends.push_back(temp_v_string);
    temp_results.xy.legends.front().push_back("ocean + nontides");

    //Copy this spectrum to the final noise results structure.
    copy_plot_line(temp_results,noise,1,1,0);//Orig plot #, line #, new plot in noise.

    //Extract separate non-tide inversion accels from verif file.
    extract_signal(temp_input,1,3);//inver(0)/verif(1) accels, next # is signal type.

    //Subtract from original verification accels.
    if(temp_input.accel.size() != verif_input.accel.size()) cout<<"WARNING!!! VERIFICATION ACCELS AREN'T THE SAME LENGTH AS ORIGINAL ACCELS!"<<endl;
    for(i=0;i<(long long)temp_input.accel.size();i++) temp_input.accel.at(i) = verif_input.accel.at(i) - temp_input.accel.at(i);

    //Calculate power spectrum.
    perform_timeseries_analysis(temp_input, temp_results);
    temp_results.xy.titles.front().append(" (Verification)");
    temp_results.xy.legends.push_back(temp_v_string);
    temp_results.xy.legends.front().push_back("nontides");

    //Copy this spectrum to the final noise results structure.
    copy_plot_line(temp_results,noise,1,1,0);//Orig plot #, line #, new plot in noise.

    //Convert orig accels + residuals to RELATIVE reduction plots.
    relative_reduction(noise);

    //***************************************************************************
    //Make a power_spectra folder in the output folder.
    plot_options_s temp_options2;
    temp_options2 = plot_options;
    noise.options.output_choice = 2;//2D plots.
    temp_options2.outputfolder.append("power_spectra/");
    create_folder(temp_options2.outputfolder);

    //Write noise results to the noise folder.
    write_output("spectra", noise, grid, temp_options2,1);

    //Might as well always copy back even if supp gr isn't disabled.
    grid=backup_grid;

  }//End of choice 1.
  else if(results.options.upward_continuation_verification_choice == 10){
    //Needs to be 2 - don't have non-region code working...
    region.options.type = 2;

    //Over-write region with regions specifically chosen for verification.
    region.options.noload=0;//Don't bother displaying it.
    region = define_region(region.options, grid, 1);

    //Over-write input because original analysis may have been performed w/
    //no regions, but region code (i.e. binindices vector) is required here
    //because I haven't written verification code to deal with a pre-split
    //input structure which would happen if regions weren't used.
    load_input(input,grid,region);
  
    if(input.options.split_input!=0) unused_input=split_input(input,region);
    
    //***************************************************************************
    //Now calculate noise reduction for inversion accels,
    //using the separate non-tide soln and then the complete soln w/o land tides.
    results_s noise, temp_results;
    temp_results.options.type=1010;
    temp_results.options.cutoff_freq=0.01;
    input_s temp_input, verif_input;
    //push_back vs of string vectors
    vector<string> temp_v_string;

    //Load original inversion accels.
    region.lat.clear();//Non-region loading is easier here.
    temp_input.options = input.options;//Foldernames, etc.
    load_input(temp_input,grid,region);
    temp_input.verification_points = input.verification_points;//Best of both worlds.
    verif_input=split_input(temp_input,region);//Set aside for later.

    //Calculate power spectrum.
    perform_timeseries_analysis(temp_input, temp_results);
    cout<<"Inversion title: "<<temp_results.xy.titles.at(0)<<endl;
    temp_results.xy.titles.at(0) = "Power averaged over many swaths";
    temp_results.xy.legends.push_back(temp_v_string);
    temp_results.xy.legends.front().push_back("Inversion");

    //Copy this spectrum to the final noise results structure.
    copy_plot_line(temp_results,noise,1,1,1);//Orig plot #, line #, new plot in noise.

    //Calculate power spectrum of original verification accels.
    perform_timeseries_analysis(verif_input, temp_results);
    cout<<"Verification title: "<<temp_results.xy.titles.at(0)<<endl;
    temp_results.xy.legends.push_back(temp_v_string);
    temp_results.xy.legends.front().push_back("Verification");

    //Copy this spectrum to the final noise results structure.
    copy_plot_line(temp_results,noise,1,1,0);//Orig plot #, line #, new plot in noise.

    //***************************************************************************
    //Make a power_spectra folder in the output folder.
    plot_options_s temp_options2;
    temp_options2 = plot_options;
    noise.options.output_choice = 2;//2D plots.
    temp_options2.outputfolder.append("power_spectra/");
    create_folder(temp_options2.outputfolder);

    //Write noise results to the noise folder.
    write_output("spectra", noise, grid, temp_options2,1);
  }//End of choice 10.
}

void copy_plot_line(results_s &temp,
                    results_s &results,
                    long long plotnum,
                    long long linenum,
                    long long newplot){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          plotnum - # of plot in temp with line to be copied
          linenum - # of line in that plot to be copied
          newplot - 0 if this line is added to last plot in results
                  - 1 if this line starts a new plot in results
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Used to push_back vectors of dbl vectors
  //vector<double> temp_v_double;
  //push_back vs of vs of dbl vectors
  vector< vector<double> > temp_vv_double;
  //push_back vs of string vectors
  vector<string> temp_v_string;
  
  //Change numbers to indices.
  plotnum--; linenum--;

  if(newplot != 0){
    results.xy.titles.push_back(temp.xy.titles.at(plotnum));
    results.xy.x_units.push_back(temp.xy.x_units.at(plotnum));
    results.xy.y_units.push_back(temp.xy.y_units.at(plotnum));
    results.xy.legends.push_back(temp_v_string);
    results.xy.x_values.push_back(temp_vv_double);
    results.xy.y_values.push_back(temp_vv_double);
  }

  results.xy.legends.back().push_back(temp.xy.legends.at(plotnum).at(linenum));
  results.xy.x_values.back().push_back(temp.xy.x_values.at(plotnum).at(linenum));
  results.xy.y_values.back().push_back(temp.xy.y_values.at(plotnum).at(linenum));
}

void extract_signal(input_s &input, long long verif, long long type){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          verif - inver(0)/verif(1) accels
          type - 1:combined residual w/o land tides, 2:ocean tides only, 3:non-tides sep. soln.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  input_s empty_input;

  long long i,k=0,l=0;
  long long onetime2;
  double oneaccel2,oneaccel3,oneaccel4,oneaccel5;
  char s2[max_length];

  //Save options and verif_pts but clear everything else in input struct.
  empty_input.options = input.options;
  empty_input.verification_points = input.verification_points;
  input = empty_input;

  //Loads verif file.
  FILE *out_fp;
  string new_file;

  char* f_return;//Used to stop fgets return value warning.

  //Open verif file in output_folder.
  new_file = gaiafolder();
  new_file.append(input.options.output_folder);
  new_file.append(input.options.output_filename);
  out_fp = fopen(new_file.c_str(),"r");
  //Warn if file doesn't open correctly.
  if(out_fp == NULL) cout<<"The verif file, "<<new_file
                         <<", failed to open."<<endl;
  else cout<<"Loading verif input data from file "
           <<new_file<<"."<<endl;

  //Read header line.
  f_return=fgets(s2,max_length,out_fp);

  i=0;k=0;l=0;
  while(fgets(s2,max_length,out_fp)!=NULL){
    //Parse line from verif file into temp vars.
    //Newer versions of perform_upward_continuation only include tides in ocean
    //and use a separate soln for the non-tide residual accels.
    //Time, combined residual w/o land tides, ocean tides, non-tides sep. soln, land tides.
    sscanf(s2,"%lld %le %le %le %le",&onetime2,&oneaccel2,&oneaccel3,&oneaccel4,&oneaccel5);

    //Is this function loading inversion or verification input data?
    if(input.verification_points.at(i) == verif){
      input.time.push_back(onetime2);
      if(type==1) input.accel.push_back(oneaccel2);
      else if(type==2) input.accel.push_back(oneaccel3);
      else if(type==3) input.accel.push_back(oneaccel4);
      else if(type==4) input.accel.push_back(oneaccel5);
      else cout<<"WARNING!!! DIDN'T RECOGNIZE TYPE!"<<endl;
    }
    i++;//Another line, another index incremented.
  }//End o loop through old input file.

  //Close verif file.
  fclose(out_fp);
}

void compare_input_files(input_s &input){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Next vars are used in RMS calculations.
  //Holds the RMS of old input with new temp input, for invers/verfc pts.
  long long i,k=0,l=0;
  double irms_orig=0.0,irms_sim=0.0,irms_resid=0.0;
  double vrms_orig=0.0,vrms_sim=0.0,vrms_resid=0.0;
  long long onetime1,onetime2;
  double onelat1,onelon1,oneaccel1,onexA,onexB,oneyA,oneyB,onezA,onezB;
  double onelat2,onelon2,oneaccel2;
  int oneascending;
  char s[max_length];
  char s2[max_length];

  long long number_warnings=0,max_warnings=100;

  char* f_return;//Used to stop fgets return value warning.

  //Loads original and temporary sim input files.
  FILE *in_fp, *out_fp;
  string old_file,new_file;

  if(input.file_comparison_method == 0){//Calc RMS straight up, no verif.
    //Open orig input file.
    old_file = gaiafolder();
    old_file.append(input.options.basefolder);
    old_file.append(input.options.filename);
    in_fp = fopen(old_file.c_str(),"r");
    //Warn if file doesn't open correctly.
    if(in_fp == NULL) cout<<"The original input data file, "<< old_file<< ", failed to open."<<endl;
    else cout<<"Loading original input data from file "<<old_file<<"."<<endl;

    //Open temp sim input file from output_folder.
    new_file = gaiafolder();
    new_file.append(input.options.output_folder);
    new_file.append(input.options.output_filename);
    out_fp = fopen(new_file.c_str(),"r");
    //Warn if file doesn't open correctly.
    if(out_fp == NULL) cout<<"The temp sim input data file, "<<new_file
                           <<", failed to open."<<endl;
    else cout<<"Loading sim input data from file "
             <<new_file<<"."<<endl;

    //Read header lines from orig and sim files.
    f_return=fgets(s,max_length,in_fp); f_return=fgets(s2,max_length,out_fp);

    //Loop through lines in original file.
    //Hopefully sim temporary input file has same number of lines...
    i=0;k=0;l=0;
    while(fgets(s,max_length,in_fp)!=NULL){
      //Complain if the sim file is too short.
      if(fgets(s2,max_length,out_fp) == NULL){
        cout<<"WARNING!  Original file is at line "<<i<<" but sim temp input file has reached the end!"<<endl;
        break;
      }
      //Parse line from orig file into temp vars.
      sscanf(s,ORBIT_READ_FORMAT, &onetime1, &onelat1, &onelon1, &oneaccel1, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);
      //Parse line from sim file into temp vars- overwrite !needed vars
      sscanf(s2,ORBIT_READ_FORMAT, &onetime2, &onelat2, &onelon2, &oneaccel2, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);

      //Convert from m/s^2 to nm/s^2.
      oneaccel1 *= 1E9; oneaccel2 *= 1E9;

      //Compare times, lats, lons.  Should all be the same.
      if(onetime1 != onetime2 and number_warnings<max_warnings){
        cout<<"!!!!WARNING- original input time is "<<onetime1<<" while sim time at index "<<i<<" is "<<onetime2<<endl;
        number_warnings++;
      }
      if(onelat1 != onelat2 and number_warnings<max_warnings){
        cout<<"!!!!WARNING- original input lat is "<<onelat1<<" while sim lat at index "<<i<<" is "<<onelat2<<endl;
        number_warnings++;
      }
      if(onelon1 != onelon2 and number_warnings<max_warnings){
        cout<<"!!!!WARNING- original input lon is "<<onelon1<<" while sim lon at index "<<i<<" is "<<onelon2<<endl;
        number_warnings++;
      }

      irms_orig += pow(oneaccel1,2);
      irms_sim += pow(oneaccel2,2);
      irms_resid += pow(oneaccel1-oneaccel2,2);
      k++;
      i++;//Another line, another index incremented.
    }//End o loop through old input file.

    //Close both input files.
    fclose(in_fp); fclose(out_fp);

    //Convert to RMSs.
    irms_orig = sqrt(irms_orig/(double)k);
    irms_sim = sqrt(irms_sim/(double)k);
    irms_resid = sqrt(irms_resid/(double)k);

    cout<<endl<<"Number of input points: "<<k<<"."<<endl;
    cout<<"Original RMS = "<<setprecision(10)<<setw(15)<<irms_orig<<" nm/s^2"<<endl;
    cout<<"Sim RMS      = "<<setprecision(10)<<setw(15)<<irms_sim<<" nm/s^2"<<endl;
    cout<<"Diff'd RMS   = "<<setprecision(10)<<setw(15)<<irms_resid<<" nm/s^2"<<endl;
    cout<<"Quality      = "<<setprecision(10)<<setw(15)<<irms_orig/irms_resid<<endl<<endl;

  }//End of input.file_comparison_method == 0
  else if(input.file_comparison_method == 1){//Calc inversion,verif RMSs
    //Open orig input file.
    old_file = gaiafolder();
    old_file.append(input.options.basefolder);
    old_file.append(input.options.filename);
    in_fp = fopen(old_file.c_str(),"r");
    //Warn if file doesn't open correctly.
    if(in_fp == NULL) cout<<"The original input data file, "<< old_file<< ", failed to open."<<endl;
    else cout<<"Loading original input data from file "<<old_file<<"."<<endl;

    //Open temp sim input file from output_folder.
    new_file = gaiafolder();
    new_file.append(input.options.output_folder);
    new_file.append(input.options.output_filename);
    out_fp = fopen(new_file.c_str(),"r");
    //Warn if file doesn't open correctly.
    if(out_fp == NULL) cout<<"The temp sim input data file, "<<new_file
                           <<", failed to open."<<endl;
    else cout<<"Loading sim input data from file "
             <<new_file<<"."<<endl;

    //Read header lines from orig and sim files.
    f_return=fgets(s,max_length,in_fp); f_return=fgets(s2,max_length,out_fp);

    //Loop through lines in original file.
    //Hopefully sim temporary input file has same number of lines...
    i=0;k=0;l=0;
    while(fgets(s,max_length,in_fp)!=NULL){
      //Complain if the sim file is too short.
      if(fgets(s2,max_length,out_fp) == NULL){
        cout<<"WARNING!  Original file is at line "<<i<<" but sim temp input file has reached the end!"<<endl;
        break;
      }
      //Parse line from orig file into temp vars.
      sscanf(s,ORBIT_READ_FORMAT, &onetime1, &onelat1, &onelon1, &oneaccel1, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);
      //Parse line from sim file into temp vars- overwrite !needed vars
      sscanf(s2,ORBIT_READ_FORMAT, &onetime2, &onelat2, &onelon2, &oneaccel2, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);

      //Convert from m/s^2 to nm/s^2.
      oneaccel1 *= 1E9; oneaccel2 *= 1E9;

      //Compare times, lats, lons.  Should all be the same.
      if(onetime1 != onetime2 and number_warnings<max_warnings){
        cout<<"!!!!WARNING- original input time is "<<onetime1<<" while sim time at index "<<i<<" is "<<onetime2<<endl;
        number_warnings++;
      }
      if(onelat1 != onelat2 and number_warnings<max_warnings){
        cout<<"!!!!WARNING- original input lat is "<<onelat1<<" while sim lat at index "<<i<<" is "<<onelat2<<endl;
        number_warnings++;
      }
      if(onelon1 != onelon2 and number_warnings<max_warnings){
        cout<<"!!!!WARNING- original input lon is "<<onelon1<<" while sim lon at index "<<i<<" is "<<onelon2<<endl;
        number_warnings++;
      }

      //Should this accel diff increment inversion or verification RMS?
      if(input.verification_points.at(i) == 0){
        irms_orig += pow(oneaccel1,2);
        irms_sim += pow(oneaccel2,2);
        irms_resid += pow(oneaccel1-oneaccel2,2);
        k++;
      }
      else{
        vrms_orig += pow(oneaccel1,2);
        vrms_sim += pow(oneaccel2,2);
        vrms_resid += pow(oneaccel1-oneaccel2,2);
        l++;
      }
      i++;//Another line, another index incremented.
    }//End o loop through old input file.

    //Close both input files.
    fclose(in_fp); fclose(out_fp);

    //Convert to RMSs.
    irms_orig = sqrt(irms_orig/(double)k);
    irms_sim = sqrt(irms_sim/(double)k);
    irms_resid = sqrt(irms_resid/(double)k);
    vrms_orig = sqrt(vrms_orig/(double)l);
    vrms_sim = sqrt(vrms_sim/(double)l);
    vrms_resid = sqrt(vrms_resid/(double)l);

    if(k!=0){
      cout<<endl<<"Number of inversion input points: "<<count(input.verification_points.begin(),input.verification_points.end(),(long long)0)<<"(check: should ="<<k<<")."<<endl;
      cout<<"Original RMS = "<<setprecision(10)<<setw(15)<<irms_orig<<" nm/s^2"<<endl;
      cout<<"Sim RMS      = "<<setprecision(10)<<setw(15)<<irms_sim<<" nm/s^2"<<endl;
      cout<<"Diff'd RMS   = "<<setprecision(10)<<setw(15)<<irms_resid<<" nm/s^2"<<endl;
      cout<<"Quality      = "<<setprecision(10)<<setw(15)<<irms_orig/irms_resid<<endl<<endl;
    }
    else cout<<endl<<"No input points were recognized as being used in the inversion process."<<endl<<endl;

    if(l!=0){
      cout<<"Number of verification input pts (not used in inversion): "<<count(input.verification_points.begin(),input.verification_points.end(),(long long)1)<<"(check: should ="<<l<<")."<<endl;
      cout<<"Original RMS = "<<setprecision(10)<<setw(15)<<vrms_orig<<" nm/s^2"<<endl;
      cout<<"Sim RMS      = "<<setprecision(10)<<setw(15)<<vrms_sim<<" nm/s^2"<<endl;
      cout<<"Diff'd RMS   = "<<setprecision(10)<<setw(15)<<vrms_resid<<" nm/s^2"<<endl;
      cout<<"Quality      = "<<setprecision(10)<<setw(15)<<vrms_orig/vrms_resid<<endl<<endl;
    }
    else cout<<endl<<"No input points were reserved as verification points (points not used in the inversion process)."<<endl<<endl;

  }//End of input.file_comparison_method == 1
}

void compare_input_file_to_verification_file(input_s &input,
                                             grid_s &grid,
                                             results_s &results){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Next vars are used in RMS calculations.
  //Holds the RMS of old input with new temp input, for invers/verfc pts.
  long long i,j=0,k=0,l=0;
  double irms_orig=0.0,irms_sim=0.0,irms_resid=0.0;
  double itrms_sim=0.0,itrms_resid=0.0;
  double intrms_sim=0.0,intrms_resid=0.0;
  double iltrms_sim=0.0,iltrms_resid=0.0;
  double inttrms_resid=0.0;//inttrms has nontides subtracted, then ocean tides.
  double intltrms_resid=0.0;//intltrms has nontides subtracted, then land tides.
  double vrms_orig=0.0,vrms_sim=0.0,vrms_resid=0.0;
  double vtrms_sim=0.0,vtrms_resid=0.0;
  double vntrms_sim=0.0,vntrms_resid=0.0;
  double vltrms_sim=0.0,vltrms_resid=0.0;
  double vnttrms_resid=0.0;//vnttrms has nontides subtracted, then ocean tides.
  double vntltrms_resid=0.0;//vntltrms has nontides subtracted, then land tides.
  long long onetime1,onetime2;
  double onelat1,onelon1,oneaccel1,onexA,onexB,oneyA,oneyB,onezA,onezB;
  double oneaccel2,oneaccel3,oneaccel4,oneaccel5;
  int oneascending;
  char s[max_length];
  char s2[max_length];

  long long number_warnings=0,max_warnings=100;

  //Loads original and temporary sim input files.
  FILE *in_fp, *out_fp;
  string old_file,new_file;

  char* f_return;//Used to stop fgets return value warning.

  double ntmean=0.0;//Mean of "orig accel - nt" Should be ~0.0, but check for Monte Carlo.
  vector<double> tide_accels;//Tides-only accels used in Monte Carlo.

  double ocean_tide_ratio = 1e-10, land_tide_ratio = 1e-10;//Absurdly small presets.
  
  long long num_tide_output = count(results.options.output_tide.begin(),results.options.output_tide.end(),(long long)1);
  long long num_nontide_output = count(results.options.output_tide.begin(),results.options.output_tide.end(),(long long)0);
  //Small_params refers to nontide-only separate solution.
  //It's only used in F-test which is disabled because the exact 
  //correlation_factor is unknown.
  //double correlation_factor = 10.0;//Bc 5s data smoothed at 50s means ~10 are corr,!indpnt
  //long long small_params = num_nontide_output*grid.lat.size();
  //big_params refers to big solution, minus tides on land. KEEP SYNCED!!!
  long long big_params = 0;
  for(i=0;i<(long long)grid.lat.size();i++){
    if(grid.wcover[i] > 0.0) big_params += results.options.output_tide.size();
    else big_params += num_nontide_output;
  }

  //Calc inversion,verification RMSs using new tides, nontides file format.
  if(input.file_comparison_method != 0){

    //Open orig input file.
    old_file = gaiafolder();
    old_file.append(input.options.basefolder);
    old_file.append(input.options.filename);
    in_fp = fopen(old_file.c_str(),"r");
    //Warn if file doesn't open correctly.
    if(in_fp == NULL) cout<<"The original input data file, "<< old_file<< ", failed to open."<<endl;
    else cout<<"Loading original input data from file "<<old_file<<"."<<endl;

    //Open temp sim input file from output_folder.
    new_file = gaiafolder();
    new_file.append(input.options.output_folder);
    new_file.append(input.options.output_filename);
    out_fp = fopen(new_file.c_str(),"r");
    //Warn if file doesn't open correctly.
    if(out_fp == NULL) cout<<"The temp sim input data file, "<<new_file
                           <<", failed to open."<<endl;
    else cout<<"Loading sim input data from file "
             <<new_file<<"."<<endl;

    //Read header lines from orig and sim files.
    f_return=fgets(s,max_length,in_fp); f_return=fgets(s2,max_length,out_fp);

    //Loop through lines in original file.
    //Hopefully sim temp input file has same num of lines... If more, not even a warning!
    i=0;k=0;l=0;
    while(fgets(s,max_length,in_fp)!=NULL){
      //Complain if the sim file is too short.
      if(fgets(s2,max_length,out_fp) == NULL){
        cout<<"WARNING!  Original file is at line "<<i<<" but sim temp input file has reached the end!"<<endl;
        break;
      }
      //Parse line from orig file into temp vars.
      sscanf(s,ORBIT_READ_FORMAT, &onetime1, &onelat1, &onelon1, &oneaccel1, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);

      //Parse line from sim file into temp vars.
      //Newer versions of perform_upward_continuation only include tides in ocean
      //and use a separate soln for the non-tide residual accels.
      //Time, combined residual w/o land tides, ocean tides, non-tides sep. soln, land tides.
      sscanf(s2,"%lld %le %le %le %le",&onetime2,&oneaccel2,&oneaccel3,&oneaccel4,&oneaccel5);

      //Convert from m/s^2 to nm/s^2.
      oneaccel1 *= 1E9; oneaccel2 *= 1E9; oneaccel3 *=1E9; oneaccel4 *=1E9; oneaccel5 *=1E9;

      //Compare times.  Should all be the same.
      if(onetime1 != onetime2 and number_warnings<max_warnings){
        cout<<"!!!!WARNING- original input time is "<<onetime1<<" while sim time at index "<<i<<" is "<<onetime2<<endl;
        number_warnings++;
      }

      //Should this accel diff increment inversion or verification RMS?
      if(input.verification_points.at(i) == 0){
        irms_orig      += pow(oneaccel1,2);
        irms_sim       += pow(oneaccel2,2);
        irms_resid     += pow(oneaccel1-oneaccel2,2);
        itrms_sim      += pow(oneaccel3,2);
        itrms_resid    += pow(oneaccel1-oneaccel3,2);
        intrms_sim     += pow(oneaccel4,2);
        intrms_resid   += pow(oneaccel1-oneaccel4,2);
        inttrms_resid  += pow(oneaccel1-oneaccel3-oneaccel4,2);//no land tides, sep nt soln
        iltrms_sim     += pow(oneaccel5,2);
        iltrms_resid   += pow(oneaccel1-oneaccel5,2);
        intltrms_resid += pow(oneaccel1-oneaccel5-oneaccel4,2);//ONLY land tides, sep nt soln
        k++;
      }
      else{
        vrms_orig      += pow(oneaccel1,2);
        vrms_sim       += pow(oneaccel2,2);
        vrms_resid     += pow(oneaccel1-oneaccel2,2);
        //Ocean-tides-only model used in Monte Carlo tests later.
        tide_accels.push_back(oneaccel3);
        vtrms_sim      += pow(oneaccel3,2);
        vtrms_resid    += pow(oneaccel1-oneaccel3,2);
        vntrms_sim     += pow(oneaccel4,2);
        vntrms_resid   += pow(oneaccel1-oneaccel4,2);
        //Record mean of "orig accel - non-tides-only" time series for Monte Carlo tests.
        ntmean+=oneaccel1-oneaccel4;
        vnttrms_resid  += pow(oneaccel1-oneaccel3-oneaccel4,2);//no land tides, sep nt soln
        vltrms_sim     += pow(oneaccel5,2);
        vltrms_resid   += pow(oneaccel1-oneaccel5,2);
        vntltrms_resid += pow(oneaccel1-oneaccel5-oneaccel4,2);//ONLY land tides, sep nt soln
        l++;
      }
      i++;//Another line, another index incremented.
    }//End o loop through old input file.

    //Close both input files.
    fclose(in_fp); fclose(out_fp);

    //So far, these are sums of squares. Save for future use.
    //double icrss = irms_resid;
    //double itrss = itrms_resid;
    //double intrss = intrms_resid;
    //double inttrss = inttrms_resid;
    //double iltrss = iltrms_resid;
    //double intltrss = intltrms_resid;
    
    //double vcrss = vrms_resid;
    //double vtrss = vtrms_resid;
    //double vntrss = vntrms_resid;
    //double vnttrss = vnttrms_resid;
    //double vltrss = vltrms_resid;
    //double vntltrss = vntltrms_resid;

    //Convert from sum of squares to RMSs.
    irms_orig      = sqrt(irms_orig/(double)k);
    irms_sim       = sqrt(irms_sim/(double)k);
    irms_resid     = sqrt(irms_resid/(double)k);
    itrms_sim      = sqrt(itrms_sim/(double)k);
    itrms_resid    = sqrt(itrms_resid/(double)k);
    intrms_sim     = sqrt(intrms_sim/(double)k);
    intrms_resid   = sqrt(intrms_resid/(double)k);
    inttrms_resid  = sqrt(inttrms_resid/(double)k);
    iltrms_sim     = sqrt(iltrms_sim/(double)k);
    iltrms_resid   = sqrt(iltrms_resid/(double)k);
    intltrms_resid = sqrt(intltrms_resid/(double)k);

    vrms_orig      = sqrt(vrms_orig/(double)l);
    vrms_sim       = sqrt(vrms_sim/(double)l);
    vrms_resid     = sqrt(vrms_resid/(double)l);
    vtrms_sim      = sqrt(vtrms_sim/(double)l);
    vtrms_resid    = sqrt(vtrms_resid/(double)l);
    vntrms_sim     = sqrt(vntrms_sim/(double)l);
    vntrms_resid   = sqrt(vntrms_resid/(double)l);
    vnttrms_resid  = sqrt(vnttrms_resid/(double)l);
    vltrms_sim     = sqrt(vltrms_sim/(double)l);
    vltrms_resid   = sqrt(vltrms_resid/(double)l);
    vntltrms_resid  = sqrt(vntltrms_resid/(double)l);

    //Also, this isn't a mean yet.
    ntmean /= (double)l;

    long precision=10,width=15;
    if(k!=0){
      long long num_inv = count(input.verification_points.begin(),input.verification_points.end(),(long long)0);
      cout<<endl<<"Number of inversion input points: "<<num_inv<<endl;
      if(k != num_inv) cout<<"WARNING! should = "<<k<<"!"<<endl;
      cout<<"Original inversion RMS    = "<<setprecision(precision)<<setw(width)<<irms_orig<<" nm/s^2"<<endl<<endl;
      if(itrms_sim>0.0){
        cout<<"Tide-only sim RMS         = "<<setprecision(precision)<<setw(width)<<itrms_sim<<" nm/s^2"<<endl;
        cout<<"Tide-only residual RMS    = "<<setprecision(precision)<<setw(width)<<itrms_resid<<" nm/s^2"<<endl;
        cout<<"Tide-only RMS drop        = "<<setprecision(precision)<<setw(width)<<100*(irms_orig-itrms_resid)/irms_orig<<" %"<<endl;
        cout<<"Tide-only var drop        = "<<setprecision(precision)<<setw(width)<<100*(1-pow(itrms_resid/irms_orig,2))<<" %"<<endl<<endl;
      }
      else cout<<"No tide parameters, so no tide-only results exist."<<endl;

      if(intrms_sim>0.0){
        cout<<"Nontide-only sim RMS      = "<<setprecision(precision)<<setw(width)<<intrms_sim<<" nm/s^2"<<endl;
        cout<<"Nontide-only residual RMS = "<<setprecision(precision)<<setw(width)<<intrms_resid<<" nm/s^2"<<endl;
        cout<<"Nontide-only RMS drop     = "<<setprecision(precision)<<setw(width)<<100*(irms_orig-intrms_resid)/irms_orig<<" %"<<endl;
        cout<<"Nontide-only var drop     = "<<setprecision(precision)<<setw(width)<<100*(1-pow(intrms_resid/irms_orig,2))<<" %"<<endl<<endl;
        //If non-tidal RMS is > 0 too, can run F-test.
        if(itrms_sim>0.0){
          cout<<"Ocean tide RMS drop rel2NT= "<<setprecision(precision)<<setw(width)<<100*(intrms_resid-inttrms_resid)/intrms_resid<<" %"<<endl;
          cout<<"Land tide RMS drop rel2NT = "<<setprecision(precision)<<setw(width)<<100*(intrms_resid-intltrms_resid)/intrms_resid<<" %"<<endl;
          ocean_tide_ratio = 1-pow(inttrms_resid/intrms_resid,2);//Now holds ocean_rel2nt_inv.
          land_tide_ratio  = 1-pow(intltrms_resid/intrms_resid,2);//Now holds land_rel2nt_inv.
          cout<<"Ocean tide var drop rel2NT= "<<setprecision(precision)<<setw(width)<<100*ocean_tide_ratio<<" %"<<endl;
          cout<<"Land tide var drop rel2NT = "<<setprecision(precision)<<setw(width)<<100*land_tide_ratio<<" %"<<endl;
          if(num_tide_output+num_nontide_output != (long long)results.options.output_tide.size()) cout<<"WARNING!!! SHOULD NEVER EVER SEE THIS!!! NDIWHB$E@#&UFH"<<endl;
          //Disabled because the exact correlation_factor is unknown.
          /*if(num_tide_output > 0 and num_nontide_output > 0){
            //Requires GSL libraries.
            #ifdef GSL_HERE
              double nu1=big_params-small_params,nu2=k/correlation_factor-big_params;
              double f_stat = (intrss - icrss)/nu1/(icrss/nu2);
              cout<<endl<<"icrss        = "<<icrss<<endl;
              cout<<"inttrss      = "<<inttrss<<endl;
              cout<<"inttrms_resid= "<<inttrms_resid<<endl;
              cout<<"intrss       = "<<intrss<<endl;
              cout<<"big_params   = "<<big_params<<endl;
              cout<<"small_params = "<<small_params<<endl;
              cout<<"k = "<<k<<endl;
              cout<<"corr_factor  = "<<correlation_factor<<endl;
              cout<<"ntmean       = "<<ntmean<<endl;
              cout<<"F inversion  = "<<setprecision(precision)<<setw(width)<<f_stat<<endl;
              //fdist_q fails if f_stat <0.
              if(f_stat>=0.0) cout<<"cdf_fdist_Q: "<<setprecision(15)<<setw(20)<<gsl_cdf_fdist_Q(f_stat,nu1,nu2)<<endl;
              else cout<<"WARNING!!! f_stat = "<<setprecision(15)<<setw(20)<<f_stat<<endl;
            #else 
              cout<<"F-test requires GSL. Search definitions.cpp for GSL_HERE."<<endl;
            #endif
          }
          else cout<<"WARNING!!! Both tidal and non-tidal rms are > 0, but num of tidal/non-tidal output params are: "<<num_tide_output<<" / "<<num_nontide_output<<endl; //*/
        }
        cout<<endl;
      }
      else cout<<"No nontide parameters, so no nontide-only results exist."<<endl;

      cout<<"Combined sim RMS          = "<<setprecision(precision)<<setw(width)<<irms_sim<<" nm/s^2"<<endl;
      cout<<"Combined residual RMS     = "<<setprecision(precision)<<setw(width)<<irms_resid<<" nm/s^2"<<endl;
      cout<<"Combined RMS drop         = "<<setprecision(precision)<<setw(width)<<100*(irms_orig-irms_resid)/irms_orig<<" %"<<endl;
      cout<<"Combined variance drop    = "<<setprecision(precision)<<setw(width)<<100*(1-pow(irms_resid/irms_orig,2))<<" %"<<endl<<endl;
    }
    else cout<<endl<<"No input points were recognized as being used in the inversion process."<<endl<<endl;

    if(l!=0){
      long long num_ver = count(input.verification_points.begin(),input.verification_points.end(),(long long)1);
      cout<<"Number of verification input points (not used in inversion): "<<num_ver<<endl;
      if(l != num_ver) cout<<"WARNING! should = "<<l<<"!"<<endl;
      cout<<"Original verification RMS = "<<setprecision(precision)<<setw(width)<<vrms_orig<<" nm/s^2"<<endl<<endl;

      if(vtrms_sim>0.0){
        cout<<"Tide-only sim RMS         = "<<setprecision(precision)<<setw(width)<<vtrms_sim<<" nm/s^2"<<endl;
        cout<<"Tide-only residual RMS    = "<<setprecision(precision)<<setw(width)<<vtrms_resid<<" nm/s^2"<<endl;
        cout<<"Tide-only RMS drop        = "<<setprecision(precision)<<setw(width)<<100*(vrms_orig-vtrms_resid)/vrms_orig<<" %"<<endl;
        cout<<"Tide-only variance drop   = "<<setprecision(precision)<<setw(width)<<100*(1-pow(vtrms_resid/vrms_orig,2))<<" %"<<endl<<endl;
      }
      else cout<<"No tide parameters, so no tide-only results exist."<<endl;

      if(vntrms_sim>0.0){
        cout<<"Nontide-only sim RMS      = "<<setprecision(precision)<<setw(width)<<vntrms_sim<<" nm/s^2"<<endl;
        cout<<"Nontide-only residual RMS = "<<setprecision(precision)<<setw(width)<<vntrms_resid<<" nm/s^2"<<endl;
        cout<<"Nontide-only RMS drop     = "<<setprecision(precision)<<setw(width)<<100*(vrms_orig-vntrms_resid)/vrms_orig<<" %"<<endl;
        cout<<"Nontide-only variance drop= "<<setprecision(precision)<<setw(width)<<100*(1-pow(vntrms_resid/vrms_orig,2))<<" %"<<endl<<endl;
        //If non-tidal RMS is > 0 too, can run F-test.
        if(vtrms_sim>0.0){
          cout<<"Ocean tide RMS drop rel2NT= "<<setprecision(precision)<<setw(width)<<100*(vntrms_resid-vnttrms_resid)/vntrms_resid<<" %"<<endl;
          cout<<"Land tide RMS drop rel2NT = "<<setprecision(precision)<<setw(width)<<100*(vntrms_resid-vntltrms_resid)/vntrms_resid<<" %"<<endl;
          double ocean_rel2nt_ver = 1-pow(vnttrms_resid/vntrms_resid,2);
          double land_rel2nt_ver  = 1-pow(vntltrms_resid/vntrms_resid,2);
          cout<<"Ocean tide var drop rel2NT= "<<setprecision(precision)<<setw(width)<<100*ocean_rel2nt_ver<<" %"<<endl;
          cout<<"Land tide var drop rel2NT = "<<setprecision(precision)<<setw(width)<<100*land_rel2nt_ver<<" %"<<endl;
          //Word the following sentences carefully depending on sign of variance reductions.
          string ocean_ver_word,ocean_inv_word,land_ver_word,land_inv_word;
          if(ocean_tide_ratio > 0 and ocean_rel2nt_ver > 0){
            ocean_inv_word = "reduce";
            ocean_ver_word = "reduce";
          }
          else if(ocean_tide_ratio > 0 and ocean_rel2nt_ver < 0){
            ocean_inv_word = "reduce";
            ocean_ver_word = "increase";
            ocean_rel2nt_ver *= -1;//Negative sign is contained in words reduce and increase.
          }
          else if(ocean_tide_ratio < 0 and ocean_rel2nt_ver > 0){
            ocean_inv_word = "INCREASE";
            ocean_ver_word = "reduce";
            ocean_rel2nt_ver *= -1;//Negative sign is contained in words reduce and increase.
          }
          else if(ocean_tide_ratio < 0 and ocean_rel2nt_ver < 0){
            ocean_inv_word = "INCREASE";
            ocean_ver_word = "increase";
          }
          if(land_tide_ratio > 0 and land_rel2nt_ver > 0){
            land_inv_word = "reduce";
            land_ver_word = "reduce";
          }
          else if(land_tide_ratio > 0 and land_rel2nt_ver < 0){
            land_inv_word = "reduce";
            land_ver_word = "increase";
            land_rel2nt_ver *= -1;//Negative sign is contained in words reduce and increase.
          }
          else if(land_tide_ratio < 0 and land_rel2nt_ver > 0){
            land_inv_word = "INCREASE";
            land_ver_word = "reduce";
            land_rel2nt_ver *= -1;//Negative sign is contained in words reduce and increase.
          }
          else if(land_tide_ratio < 0 and land_rel2nt_ver < 0){
            land_inv_word = "INCREASE";
            land_ver_word = "increase";
          }
          ocean_tide_ratio = ocean_rel2nt_ver/ocean_tide_ratio;//Already holds ocean_rel2nt_inv.
          land_tide_ratio = land_rel2nt_ver/land_tide_ratio;//Already holds land_rel2nt_inv.
          cout<<"The ocean tides "<<ocean_ver_word<<" the variance of the verification accelerations by "<<setprecision(3)<<100*ocean_tide_ratio<<" % as much as they "<<ocean_inv_word<<" the variance of inverted accelerations."<<endl;
          cout<<"The land tides, however, "<<land_ver_word<<" the variance of the verification accelerations by "<<setprecision(3)<<100*land_tide_ratio<<" % as much as they "<<land_inv_word<<" the variance of inverted accelerations."<<endl;
          if(num_tide_output+num_nontide_output != (long long)results.options.output_tide.size()) cout<<"WARNING!!! SHOULD NEVER EVER SEE THIS!!! NDIWHB$E@#&UFH"<<endl;
          //Disabled because the exact correlation_factor is unknown.
          /*if(num_tide_output > 0 and num_nontide_output > 0){
            //Requires GSL libraries.
            #ifdef GSL_HERE
              double nu1=big_params-small_params,nu2=l/correlation_factor-big_params;
              double f_stat = (vntrss - vcrss)/nu1/(vcrss/nu2);
              cout<<endl<<"vcrss        = "<<vcrss<<endl;
              cout<<"vnttrss      = "<<vnttrss<<endl;
              cout<<"vnttrms_resid= "<<vnttrms_resid<<endl;
              cout<<"vntrss       = "<<vntrss<<endl;
              cout<<"l = "<<l<<endl;
              cout<<"F verif      = "<<setprecision(precision)<<setw(width)<<f_stat<<endl;
              //fdist_q fails if f_stat <0.
              if(f_stat>=0.0) cout<<"cdf_fdist_Q: "<<setprecision(15)<<setw(20)<<gsl_cdf_fdist_Q(f_stat,nu1,nu2)<<endl;
              else cout<<"WARNING!!! f_stat = "<<setprecision(15)<<setw(20)<<f_stat<<endl;
            #else 
              cout<<"F-test requires GSL. Search definitions.cpp for GSL_HERE."<<endl;
            #endif
          }
          else cout<<"WARNING!!! Both tidal and non-tidal rms are > 0, but num of tidal/non-tidal output params are: "<<num_tide_output<<" / "<<num_nontide_output<<endl; //*/
        }
        cout<<endl;
      }
      else cout<<"No nontide parameters, so no nontide-only results exist."<<endl;

      cout<<"Combined sim RMS          = "<<setprecision(precision)<<setw(width)<<vrms_sim<<" nm/s^2"<<endl;
      cout<<"Combined residual RMS     = "<<setprecision(precision)<<setw(width)<<vrms_resid<<" nm/s^2"<<endl;
      cout<<"Combined RMS drop         = "<<setprecision(precision)<<setw(width)<<100*(vrms_orig-vrms_resid)/vrms_orig<<" %"<<endl;
      cout<<"Combined variance drop    = "<<setprecision(precision)<<setw(width)<<100*(1-pow(vrms_resid/vrms_orig,2))<<" %"<<endl<<endl;

      //Monte Carlo tests to see how often vnttrss should be lower than vntrss by the
      //observed amount.
      if(vntrms_sim>0.0 and vtrms_sim>0.0){
        //Requires GSL libraries.
        #ifdef GSL_HERE
          long long mcnum = 100;
          long long mclen = l;
          if(mclen > l) cout<<"WARNING!!! MCLEN CAN'T BE > L!"<<endl;
          double vdrop = 1-pow(vnttrms_resid/vntrms_resid,2);
          double cvdrop=0;//Current variance drop, one for each MC test.
          double cvdropmax=-9999999;//Biggest variance drop in all MC tests.
          double cvdropavg=0.0;//Average variance drop in all MC tests.
          long long bigcnt=0,smlcnt=0,negcnt=0;
          const gsl_rng_type * R;
          gsl_rng * r;
          
          //Initialize variables for notification function for time remaining.
          notification_s notification;
          //1st notification occurs after two loops, but it usually won't print
          //anything because not enough total_time will have passed (first_interval).
          //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
          notification.interval=2;
          //Desired min and max intervals in seconds b/w notifications.
          notification.first_interval=10;
          notification.min_interval=600, notification.max_interval=900;
          //notification.min_interval=30, notification.max_interval=90;
          notification.start_time=-1; notification.current_time=-1;
          notification.previous_time=-1;//Needs to be initialized
          notification.prefix = "Monte Carlo";

          notification.starting_index = 0;
          notification.partial_saves_enabled = 0;

          //Used to size notification intervals.
          notification.previous_i=notification.starting_index;
          notification.num_loops = mcnum-notification.starting_index;
          //notification.interval will not be allowed to go above this number
          //so that at LEAST a certain number of messages are seen.
          notification.max_notif_interval=(long long)(notification.num_loops/4);

          //Annoyingly, these need to be defined! Argh!
          least_squares_s dummy_ls;
          input_s dummy_input;
          results_s dummy_ar;

          notification.start_time = time(NULL);

          gsl_rng_env_setup();
          R = gsl_rng_default;
          r = gsl_rng_alloc(R);
          gsl_rng_set(r,time(0));
          //Loop through all Monte Carlo tests.
          for(i=0;i<mcnum;i++){
            notify(notification,i,dummy_ls,dummy_input,dummy_ar);
            double cvar1=0.0,cvar2=0.0;//Current variance of noise and noise-tide model.
            double cmean=0.0;//Current mean of noise.
            //Create gaussian noise time series.
            for(j=0;j<mclen;j++){
              double randnum = ntmean + gsl_ran_gaussian(r,vntrms_resid);
              cmean+=randnum;
              cvar1 += pow(randnum,2);
              cvar2 += pow(randnum-tide_accels.at(j),2);
            }
            cmean/=(double)mclen;
            cvar1/=(double)mclen; cvar2/=(double)mclen;
            //cout<<"cmean       = "<<cmean<<endl;
            //cout<<"cvar1       = "<<cvar1<<endl;
            //cout<<"cvar2       = "<<cvar2<<endl;
            
            //Calculate variance drop after subtracting tide-only accels.
            cvdrop = (cvar1-cvar2)/cvar1;
            cvdropavg += cvdrop;
            if(cvdrop > cvdropmax){
              cvdropmax = cvdrop;
            }

            //cout<<"Run #"<<i+1<<" had variance drop of "<<cvdrop*100<<" %."<<endl;
            if(cvdrop>vdrop){
              bigcnt++;
            }
            else if(cvdrop>0.0){
              smlcnt++;
            }
            else{
              negcnt++;
            }
          }

          cvdropavg /= (double)mcnum;//Until this point, just a sum of cvdrops.

          cout<<"bigcnt: "<<bigcnt<<", smlcnt: "<<smlcnt<<", negcnt: "<<negcnt<<endl;
          cout<<"Maximum variance drop: "<<cvdropmax*100<<" %."<<endl;
          cout<<"Average variance drop: "<<cvdropavg*100<<" %."<<endl;

        #else 
          cout<<"Monte Carlo tests require GSL. Search definitions.cpp for GSL_HERE."<<endl;
        #endif
      }
    }
    else cout<<endl<<"No input points were reserved as verification points (points not used in the inversion process)."<<endl<<endl;

  }//End of input.file_comparison_method != 0 
}

void add_residuals_to_fes(results_s &results, grid_s &grid, model_s &model){
  /**********************************************************************
  Purpose: This function modifies the *.asc files that FES uses to store
          amplitudes and phases of major tides by adding my mascon residual
          signal to those amplitudes and phases.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k;
  vector < vector<long long> > nearest_neighbors;
  vector<long long> temp_vector;//Used to push_back vector of vectors
  vector<long long> current_neighbors;
  vector<double> current_angles;

//CHANGE THAT RESIDUAL_ADD_TO_FES FUNCTION (IT CURRENTLY MAKES ITS OWN LIST OF NEARBY GRID POINTS!!) SO THAT IT USES THE ZONE OF INFLUENCE!!!! STUPID!!!
//CHANGE THAT RESIDUAL_ADD_TO_FES FUNCTION (IT CURRENTLY MAKES ITS OWN LIST OF NEARBY GRID POINTS!!) SO THAT IT USES THE ZONE OF INFLUENCE!!!! STUPID!!!
//CHANGE THAT RESIDUAL_ADD_TO_FES FUNCTION (IT CURRENTLY MAKES ITS OWN LIST OF NEARBY GRID POINTS!!) SO THAT IT USES THE ZONE OF INFLUENCE!!!! STUPID!!!
  //For each grid pt, find nearest neighboring grid pts.
  long long num_neighbors = 8;//Number of neighboring grid pts around each grid pt.
  for(i=0;i<(long long)grid.lat.size();i++){
    nearest_neighbors.push_back(temp_vector);
    current_neighbors.clear(); current_angles.clear();
    current_neighbors.resize(num_neighbors,-1);
    current_angles.resize(num_neighbors,twoPi);
    for(j=0;j<(long long)grid.lat.size();j++){
      if(i != j){//Current grid point is NOT a neighbor of current grid pt.
        double current_angle = spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j));
        k=0;
        while(k<num_neighbors){
          if(current_angle < current_angles.at(k)){
            current_angles.at(k) = current_angle;
            current_neighbors.at(k) = j;
            k = num_neighbors;//Exit this while loop now for speed reasons.
          }
          else k++;//Otherwise, compare this angle to next largest angle.
        }
      }
      //Record this grid pt's nearest neighbors in permanent vec of vecs.
      nearest_neighbors.at(i) = current_neighbors;
    }
  }

  match_model_and_results_parameters(results, model);

  //Loop through rawfes pts, find which bin it's in. Then find out which
  //of the nearest neighbors of that bin are closest. Then interpolate residual
  //amp/phase (sincos conv?) and add (how?) then save.
}

void match_model_and_results_parameters(results_s &results,
                                        model_s &model){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
model.options.type++; model.options.type--;
results.options.type++; results.options.type--;

}

double spherical_distance(double lat1, double lon1, double lat2, double lon2){
  /**********************************************************************
  Purpose: This function calculates the central angle (in radians) between
          two points on a sphere, given their coordinates.  It uses the
          haversine formula as found on Wikipedia under spherical distance.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          double lat1 - Latitude of point 1 (in degrees- 0 at equator)

          double lon1 - Longitude of point 1 (in degrees)

          double lat2 - Latitude of point 2 (in degrees- 0 at equator)

          double lon2 - Longitude of point 2 (in degrees)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          double central_angle - Angle (rad) between the two points.
  **********************************************************************/
  double central_angle;//Result of Haversine formula.

  //Convert coordinates to radians.
  lat1 = lat1*deg2rad;
  lon1 = lon1*deg2rad;
  lat2 = lat2*deg2rad;
  lon2 = lon2*deg2rad;

  //Calculate central_angle (in radians) from Haversine formula.
  central_angle = 2*asin(sqrt(pow(sin((lat1-lat2)/2),2)+cos(lat1)*cos(lat2)*pow(sin((lon1-lon2)/2),2)));

  return central_angle;
}

void spherical_latlon2xyz(transform_s &transform){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  double colat=deg2rad*(90.0-transform.lat),lon=deg2rad*transform.lon;

  //Unit radius.
  transform.x=cos(lon)*sin(colat);
  transform.y=sin(lon)*sin(colat);
  transform.z=cos(colat);

  //cout<<"x,y,z after conversion from latlon: "<<transform.x<<", "<<transform.y<<", "<<transform.z<<endl;
}

void spherical_xyz2latlon(transform_s &transform){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  double colat,lon;

  colat=atan2(sqrt(pow(transform.x,2)+pow(transform.y,2)),transform.z);
  lon=atan2(transform.y,transform.x);

  //cout<<"temp colat/lon (in rads?): "<<colat<<" / "<<lon<<endl;

  //Now convert from rad2deg and colat to lat!
  transform.lat=90.0-rad2deg*colat;
  transform.lon=rad2deg*lon;

  //Any lon > 180 is mapped to -180,0. Fix that.
  if(transform.lon<0) transform.lon += 360.0;
}

void rotate_xyz_about_x(transform_s &transform){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  double yold=transform.y;

  if(transform.init!=1){
    transform.costheta = cos(deg2rad*transform.theta);
    //cout<<"costheta: "<<transform.costheta<<endl;
    transform.sintheta = sin(deg2rad*transform.theta);
    //cout<<"sintheta: "<<transform.sintheta<<endl;
    transform.init=1;
  }

  transform.y = yold*transform.costheta-transform.z*transform.sintheta;
  transform.z = yold*transform.sintheta+transform.z*transform.costheta;
  //cout<<"x,y,z after rotation: "<<transform.x<<", "<<transform.y<<", "<<transform.z<<endl;
}

string create_grid_cache_filename(grid_options_s &grid_options){
  /**********************************************************************
  Purpose: This function is called within define_grid to create a filename
          which is then used to save the current grid data to disk.  Thus,
          future runs using the EXACT same grid_options parameters will
          be able to load the grid from disk rather than re-computing the
          grid from scratch each time.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string filename - Contains the filename (no path!).
  **********************************************************************/
  string filename;

  //Format is:
  // "options-grid_options_substring.txt"
  //If this changes, adjust ropos, rosize, topopos, toposize in ls_cache_init!
  filename = create_grid_options_substring(grid_options);
  filename.append(".txt");
  return filename;
}

string create_grid_options_substring(grid_options_s &grid_options){
  /**********************************************************************
  Purpose: This function is called in create_grid_cache_filename to make a
          string of grid_options that go in most cache filenames.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string filename - Contains PART of a filename.
  **********************************************************************/
  string filename;
  char str[max_length];

  //Format is:
  // "AAYYZZCCCCCCDDDDDDEEEEEEEEEEFFFFFFGGGGGGHHHHHHIIIIIIJJJJJJJJJJKKKKKKKKKK"
  //AA            - grid type
  //YY            - topo type - copy of ancillary.topo.type
  //ZZ            - grid area_type
  //CCCCCCCC      - orig_lat_spacing
  //DDDDDDDC      - orig_lon_spacing
  //EEEEEEEE      - orig_cap_size
  //FFFFFF        - grid_options.boundaries[0]
  //GGGGGG        - grid_options.boundaries[1]
  //HHHHHH        - grid_options.boundaries[2]
  //IIIIII        - grid_options.boundaries[3]
  //JJJJJJJJJJ    - grid_options.specified_lat
  //KKKKKKKKKK    - grid_options.specified_lon
  //If this changes, adjust ropos, rosize, topopos, toposize in ls_cache_init!
  sprintf(str,
    "%02d%02d%02d%08.2f%08.2f%08.2f%06.2f%06.2f%06.2f%06.2f%010.5f%010.5f",
    grid_options.type,grid_options.topo_type,grid_options.area_type, grid_options.original_lat_spacing,grid_options.original_lon_spacing, grid_options.original_cap_size, grid_options.boundaries[0], grid_options.boundaries[1], grid_options.boundaries[2], grid_options.boundaries[3], grid_options.specified_lat, grid_options.specified_lon);
  filename=str;//Need to convert.
  return filename;
}

string create_support_grid_cache_filename(grid_options_s &grid_options){
  /**********************************************************************
  Purpose: This funct. is called in define_support_grid to create a filename
          which is then used to save the current grid data to disk.  Thus,
          future runs using the EXACT same grid_options parameters will
          be able to load the grid from disk rather than re-computing the
          grid from scratch each time.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string filename - Contains the filename (no path!).
  **********************************************************************/
  string filename,temp;
  char str[max_length];

  //Format is:
  // "main-grid_options_substring-support-AABBCC.txt"
  //support
  //AA            - support_type
  //BB            - support_lat_multiplier
  //CC            - support_lon_multiplier
  temp = create_grid_options_substring(grid_options);
  filename="main-";
  filename.append(temp);
  //If this changes, adjust ropos, rosize, topopos, toposize in ls_cache_init!
  sprintf(str,"-support-%02d%02d%02d.txt", grid_options.support_type, grid_options.support_lat_multiplier, grid_options.support_lon_multiplier);
  filename.append(str);
  return filename;
}

string create_model_cache_filename(grid_s &grid, model_s &model){
  /**********************************************************************
  Purpose: This function is called within load_model to create a filename
          which is then used to save the current model data to disk.  Thus,
          future runs using the EXACT same model and grid options will
          be able to load the model from disk rather than re-loading the
          model straight from the original data files each time.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string filename - Contains the filename (no path!).
  **********************************************************************/
  string filename,temp;
  char str[max_length];

  //Format is "grid-grid_options_substring-JJJKKKKKKLLLLLL.txt"
  //The next field is reserved for model type (fes, r. ray, padman, etc).
  //JJJ      - model.type
  //The next field holds the halfwidth, used for Gaussian smoothing.
  //KKKKKK   - model.options.halfwidth
  //The next field holds the averaging_radius, used for averaging.
  //LLLLLL   - model.options.averaging_radius
  temp = create_grid_options_substring(grid.options);
  filename="grid-";
  filename.append(temp);
  sprintf(str,"-%03d%06.2f%06.2f.txt",model.options.type, model.options.halfwidth, model.options.averaging_radius);
  filename.append(str);
  return filename;
}

string create_region_cache_filename(grid_options_s &region_options,
                                    grid_options_s &grid_options){
  /**********************************************************************
  Purpose: This function is called within define_region to create a filename
          which is used to save the current region data to disk.  Thus,
          future runs using the EXACT same region_options parameters will
          be able to load the region from disk rather than re-computing the
          region from scratch each time.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string filename - Contains the filename (no path!).
  **********************************************************************/
  string filename,temp;

  //Format"grid-grid_options_substring-region-region_options_substring.txt"
  temp = create_grid_options_substring(grid_options);
  filename="grid-";
  filename.append(temp);
  filename.append("-region-");
  //If this changes, adjust ropos, rosize, topopos, toposize in ls_cache_init!
  temp = create_region_options_substring(region_options);
  filename.append(temp);
  filename.append(".txt");
  return filename;
}

string create_region_options_substring(grid_options_s &region_options){
  /**********************************************************************
  Purpose: This function is called in create_region_cache_filename to make a
          string of region_options that go in some cache filenames.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string filename - Contains the filename (no path!).
  **********************************************************************/
  string filename;
  char str[max_length];

  //Format: AACCCCCCCDDDDDDDEEEEEEEJJJJJJJ
  //AA       - type
  //CCCCCCC  - ORIGINAL lat_spacing (before autosize)
  //DDDDDDD  - ORIGINAL lon_spacing (before autosize)
  //EEEEEEE  - ORIGINAL cap_size    (before autosize)
  //JJJJJJJ  - overlap_distance
  //I've moved to 7.2 format for regions because the lat and lon spacing
  //goes up above 1000km, as well as overlap distance.  It's just easier to
  //have all of them at 7.2.
  //If this changes, adjust ropos, rosize, topopos, toposize in ls_cache_init!
  sprintf(str,
    "%02d%07.2f%07.2f%07.2f%07.2f",
    region_options.type, region_options.original_lat_spacing,
    region_options.original_lon_spacing, region_options.original_cap_size,
    region_options.overlap_distance);
  filename=str;//Need to convert.
  return filename;
}

string create_input_cache_filename(input_options_s &input_options,
                                   grid_options_s &grid_options){
  /**********************************************************************
  Purpose: This function is called within load_input to create a
          filename which is then used to save the current input binindices
          data (etc) to disk.  Thus, future runs using the EXACT same
          input and grid_options parameters will be able to load the input
          data from disk rather than re-computing the it from scratch.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string filename - Contains the filename (no path!).
  **********************************************************************/
  string filename,temp;
  char str[max_length];

  //Format is "grid-grid_options_substring-input_filename-FILENAME"
  //The extension on the input filename becomes the
  //extention of this type of cache file.
  temp = create_grid_options_substring(grid_options);
  filename="grid-";
  filename.append(temp);
  sprintf(str,"-input_filename-%s",input_options.filename.c_str());
  filename.append(str);
  return filename;
}

string create_tg_cache_filename(insitu_s &insitu){
  /**********************************************************************
  Purpose: This function is called within load_model to create a filename
          which is then used to save the current model data to disk.  Thus,
          future runs using the EXACT same model and grid options will
          be able to load the model from disk rather than re-loading the
          model straight from the original data files each time.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string filename - Contains the filename (no path!).
  **********************************************************************/
  string filename;
  //TEMP - JUST SO INSITU IS TOUCHED.
  insitu.noload++; insitu.noload--;
  filename="tide_gauge.txt";
  return filename;
}

string create_least_squares_cache_foldername(
                      grid_options_s &region_options,
                      grid_options_s &grid_options,
                      results_s &results){
  /**********************************************************************
  Purpose: This function is called within least_squares_init to create
           the foldername appropriate to the current options.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string foldername - Contains the foldername (no path!).
  **********************************************************************/
  string foldername,temp;
  
  char str[max_length];//Temp storage.
  int pos;

  foldername = current_datetime();
  foldername.append("-");
  //Format is "20090907-174632-ls-AAA-region_cache_filename" (name w/o .txt)

  //Starts with time in little-endian format to avoid creating duplicate
  //foldernames and so the time is human-readable.

  //least squares (ls).
  //AAA       - results.options.h_matrix_choice

  //region_cache_filename has format:
  //"grid-grid_options_substring-region-region_options_substring.txt"

  sprintf(str,"ls-%03d-", results.options.h_matrix_choice);
  foldername.append(str);
  temp=create_region_cache_filename(region_options,grid_options);
  //As stated above, remove ".txt" from region cache filename.
  if((pos=temp.find(".txt"))!=(int)string::npos) temp.erase(pos,4);
  else cout<<"!!!WARNING!!! SHOULD NEVER SEE THIS!!! .TXT SHOULD ALWAYS BE IN REGION CACHE FILENAME, RIGHT?!!? JV783ND832H3"<<endl;
  foldername.append(temp);
  return foldername;
}

string create_least_squares_cache_filename(results_s &results){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  char s[max_length];//Temp storage.
  string filename;

  //Note that the number in the file is the region index PLUS ONE.
  sprintf(s,"Region_%06lld.txt",results.options.region_index+1);
  filename=s;
  return filename;
}

string create_arbitrary_cache_filename(input_s &input){
  /**********************************************************************
  Purpose: This function creates a filename which is used to save the 
          interpolated arbitrary time series to disk.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string filename - Contains the filename (no path!).
  **********************************************************************/
  string filename;
  char str[max_length];
  long long i;

  //Format is "arbitrary_params-10001-...-10002-input_filename-FILENAME"
  //The extension on the input filename becomes the
  //extention of this type of cache file.
  filename="arbitrary_params-";
  for(i=0;i<(long long)input.arbitrary.y_values.size();i++){
    if((long long)input.arbitrary.y_values.at(i).size()>0){
      sprintf(str,"%05d-",input.arbitrary.parameter_type.at(i));
      filename.append(str);
    }
  }
  sprintf(str,"input_filename-%s",input.options.filename.c_str());
  filename.append(str);
  return filename;
}

int readdoubles(char* line, const int sizeOf, double* values){
  /**********************************************************************
  Purpose: This function was copied from the FES source code.  It takes a
          string and parses it into multiple values, which are assumed to
          be doubles.

          Originally called readValues() in grid.c and ascii2bin.c.
  ***********************************************************************
  Input:
          line   - line to read
          sizeOf - Actually the NUMBER of values in "line".
          values - The data read.
  ***********************************************************************
  Output:
          Return value contains the number of input fields successfully
          scanned and converted into doubles.  "values" is passed as a
          pointer, so it also functions as output, returning the values
          that have been converted.
  **********************************************************************/
  int   ix      = 0;
  char* token   = strtok(line, " \t");

  while(token){
    if( ix > sizeOf ) return -1;

    if(sscanf(token, "%lf", &values[ix]) != 1) return ix;

    ix++;

    token  = strtok(NULL, " \t");
  }
  return ix;
}

/*
// ///////////////////////////////////////////////////////////////////////////
// Computes square elevation.
//
// Parameters:
//	x:	value
//
// Return value:
//   value²
*/
double sqr(const double x){
    return x * x;
}

// ///////////////////////////////////////////////////////////////////////////
// This program initialize some astronomic data useful for
// nodal corrections.
//   itj	Desired UTC time, in (decimal) Modified.
//   tt		Mean solar angle relative to Greenwich
//   hp		Mean solar longitude
//   s		Mean lunar longitude
//   p1		Longitude of solar perigee
//   p		Longitude of lunar perigee</param>
//   iang
//   xi
//   nu
//   x1ra
//   r
//   nuprim
//   nusec
void astronomics(const double  itj, astronomicAngle* angle){
//NOTE: "itj" appears to be in JulianCenturies rel to 1900-1-1.0.
    static const double ct0          = 180.0;
    static const double ct1          = 360.0 * 3.6525E+04;
    static const double cn0          = 259.1560563;
    static const double cn1          = -1934.1423972;
    static const double cs0          = 277.0256206;
    static const double cs1          = 481267.892;
    static const double ch0          = 280.1895015;
    static const double ch1          = 36000.76892;
    static const double cps0         = 281.2208568;
    static const double cps1         = 1.719175;
    static const double cp0          = 334.3837214;
    static const double cp1          = 4069.0322056;
    double              tgn2;
    double              at1;
    double              at2;
    double              u;
    double              tgi2;
    double              n;
    double              pp;

    // tt mean solar angle relative to Greenwich
    angle->tt = fmod(ct0 + ct1 * itj, 360.0) * deg2rad;

    // hp longitude of ascending lunar node
    n = fmod(cn0 + cn1 * itj, 360.0) * deg2rad;

    // hp mean solar longitude
    angle->hp = fmod(ch0 + ch1 * itj, 360.0) * deg2rad;

    // s mean lunar longitude
    angle->s = fmod(cs0 + cs1 * itj, 360.0) * deg2rad;

    // p1 longitude of solar perigee
    angle->p1 = fmod(cps0 + cps1 * itj, 360.0) * deg2rad;

    // p longitude of lunar perigee
    angle->p = fmod(cp0 + cp1 * itj, 360.0) * deg2rad;

    u = 9.13694997e-01 - 3.5692561e-02 * cos(n);

    angle->iang = acos(u);

    tgn2 = tan(n / 2.0);

    at1 = atan(1.01883 * tgn2);
    at2 = atan(6.4412e-01 * tgn2);

    angle->xi = -at1 - at2 + n;

    if ( n > M_PI )
    {
	angle->xi -= 2.0 * M_PI;
    }

    angle->nu = at1 - at2;

    // for constituents l2,k1,k2
    tgi2 = tan(angle->iang / 2.0);

    pp = angle->p - angle->xi;

    angle->x1ra = sqrt(1.0 - 12.0 * sqr(tgi2) * cos(2.0 * pp) + 36.0 *
	pow(tgi2, 4));

    angle->r = atan(sin(2.0 * pp) / (1.0/(6.0 * sqr(tgi2)) - cos(2.0 * pp)));

    angle->nuprim = atan(sin(2.0 * (angle->iang)) * sin(angle->nu) /
	(sin(2.0 * (angle->iang)) * cos(angle->nu) + 3.347E-01));

    angle->nusec = 0.5 * atan(((sqr(sin(angle->iang))) * sin(2.0 * (angle->nu))) /
	(sqr(sin(angle->iang)) * cos(2.0 * (angle->nu))+ 7.27E-02));

}

void cp_doodson(int doodson_numbers[], period_info_s &period_info){
  /**********************************************************************
  Purpose: This function copies Doodson numbers to period_info.
  ***********************************************************************
  Input: (types ending in "_s" are defined in definitions.hpp)
         double period - Period in question, in seconds.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  period_info.doodson.numbers.clear();
  period_info.doodson.numbers.resize(7,0);
  for(int i=0;i<6;i++){
    period_info.doodson.numbers.at(i+1) = doodson_numbers[i];
  }
}

void greenwich_phase_at_epoch(doodson_s &doodson){
  /**********************************************************************
  Purpose: This function calculates the Greenwich phase of TGP at the time
          in doodson.time, given in UTC seconds relative to J2000.
          Eqns described in Shailen Desai's thesis, eq 2.31, table 2.1.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  int i;//Counter through Doodson vectors which are quite short.

  doodson.long_titles.clear(); doodson.long_titles.resize(7);
  doodson.short_titles.clear(); doodson.short_titles.resize(7);

  doodson.long_titles.at(1)  = "Mean lunar time (deg)";
  doodson.short_titles.at(1) = "Lunar time";

  doodson.long_titles.at(2)  = "Mean lunar longitude (deg)";
  doodson.short_titles.at(2) = "Lunar lon";

  doodson.long_titles.at(3)  = "Mean solar longitude (deg)";
  doodson.short_titles.at(3) = "Solar lon";

  doodson.long_titles.at(4)  = "Mean lunar perigee longitude (deg)";
  doodson.short_titles.at(4) = "Lun per lon";

  doodson.long_titles.at(5)  = "Negative mean longitude of ascending lunar node (deg)";
  doodson.short_titles.at(5) = "Asc lun node";

  doodson.long_titles.at(6)  = "Mean solar perigee longitude (deg)";
  doodson.short_titles.at(6) = "Solar per lon";

  //For easy retrieval in loops.
  doodson.args.clear(); doodson.args.resize(7,0.0);

  //For calculating periods based on Doodson numbers.
  doodson.omegas.clear(); doodson.omegas.resize(7,0.0);

  switch(doodson.type){
    default: cout<<"!!!!WARNING!!!!!! Doodson type "<<doodson.type<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{//Shailen's thesis

      //This is the epoch used for my Doodson argument calculation, based on
      //Shailen's thesis eq. 2.31 and table 2.1.
      //Reference epoch: 1960-05-22.5.
      //./cal2sec.e 1960 05 22 12 00
      //-1250035200.0000000
      //Following value is only for these values from Shailen's thesis.
      long long doodson_epoch = -1250035200;

      //Formalism and numbers from Shailen's thesis, pages 21-22.

      //Centuries, squared centuries, days since doodson_epoch.
      double cy   = (doodson.time - doodson_epoch)*sec2jcy;
      double cy2  = cy*cy;
      double cy3  = cy2*cy;
      double days = cy*jcy2jday;

      //As in equation 2.25, values fom table 2.1 in Shailen's thesis.
      //As usual, index 0 is empty.
      double a[7],b[7],c[7],d[7];

      //Degrees.
      a[2]   =  22.22101;
      a[3]   =  60.11923;
      a[1]   = 180.0 - a[2] + a[3];//Out of order b/c depends on 2,3- see eq. 2.27.
      a[4]   = 271.56503;
      a[5]   = 188.82048;
      a[6]   = 282.25919;

      //Degrees per Julian day(solar?).
      b[2] =  13.17639648;
      b[3] =   0.98564736;
      b[1] = 360.0 - b[2] + b[3];//Out of order b/c depends on 2,3- see eq. 2.26.
      b[4] =   0.11140353;
      b[5] =   0.05295377;
      b[6] =   0.00004706;

      //Converts from b[] units to rad/sec.
      long double omega_conversion = deg2rad*(sec2jday);

      //Degrees per Julian centuries squared.
      c[1]     =   0.000000;
      c[2]     =  -0.001133;
      c[3]     =   0.000303;
      c[4]     =  -0.010325;
      c[5]     =  -0.002078;
      c[6]     =   0.000450;

      //Degrees per Julian centuries cubed.
      d[1]     =   0.0000000;
      d[2]     =   0.0000019;
      d[3]     =   0.0000000;
      d[4]     =  -0.0000120;
      d[5]     =   0.0000000;
      d[6]     =   0.0000030;

      //Mean lunar time (UNITS?!??).
      i=1;
      doodson.tau=restrict_degrees(a[i]+b[i]*days+c[i]*cy2+d[i]*cy3);
      doodson.args.at(i) = doodson.tau;

      //Mean lunar lon (deg).
      i=2;
      doodson.s=restrict_degrees(a[i]+b[i]*days+c[i]*cy2+d[i]*cy3);
      doodson.args.at(i) = doodson.s;

      //Mean solar lon (deg).
      i=3;
      doodson.h=restrict_degrees(a[i]+b[i]*days+c[i]*cy2+d[i]*cy3);
      doodson.args.at(i) = doodson.h;

      //Mean lunar perigee lon (deg).
      i=4;
      doodson.p=restrict_degrees(a[i]+b[i]*days+c[i]*cy2+d[i]*cy3);
      doodson.args.at(i) = doodson.p;

      //Negative mean lon of ascending lunar node (deg).
      i=5;
      doodson.np=restrict_degrees(a[i]+b[i]*days+c[i]*cy2+d[i]*cy3);
      doodson.args.at(i) = doodson.np;

      //Mean solar perigee lon (deg).
      i=6;
      doodson.ps=restrict_degrees(a[i]+b[i]*days+c[i]*cy2+d[i]*cy3);
      doodson.args.at(i) = doodson.ps;

      //Convert these particular b[]'s into rad/sec.
      for(i=1;i<(int)doodson.omegas.size();i++){
        doodson.omegas.at(i) = b[i]*omega_conversion;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 2:{//FES code

      //This is the epoch used for my Doodson argument calculation.
      long long doodson_epoch = fes_utc_int_epoch;

      //Centuries, squared centuries, days since FES internal epoch.
      double cy   = (doodson.time - doodson_epoch)*sec2jcy;

      astronomicAngle astro;
      astronomics(cy, &astro);
      astro.tt            *= rad2deg;
      astro.hp            *= rad2deg;
      astro.s             *= rad2deg;
      astro.p1            *= rad2deg;
      astro.p             *= rad2deg;
      astro.xi            *= rad2deg;
      astro.nu            *= rad2deg;
      astro.r             *= rad2deg;
      astro.nuprim        *= rad2deg;
      astro.nusec         *= rad2deg;

      double b[7];

      //Degrees per Julian century?
      //Copied from astronomics() in prediction.c.
      //NOT THSE, THOUGH, THEY'RE COPIED FROM CASE 3!!! CHANGE OMEGA_CONV TOO!!
      b[1] =  127037328.88553056;
      b[2] =    4812678.81195750;
      b[3] =     360007.69748806;
      b[4] =      40690.13635250;
      b[5] =      19341.36261972;
      b[6] =         17.19457667;

      //Converts from b[] units to rad/sec.
      long double omega_conversion = deg2rad/(sec2jcy/10.0);

      /*
      // Compute 4 principal mean longitudes in radians at time TD
      //ts : Julian day (days since 1950-01-01 00:00:00.000 UTC).
      td = ((ts + 33282.0) * 86400.0 - 4043174400.0) / 86400.0;

      //Coeff from R. Ray at 0h on 1/1/1990 in degrees
      shpn[0] = deg2rad * fmod(290.210 + (td * 13.17639650), 360.0);	// S
      shpn[1] = deg2rad * fmod(280.120 + (td *  0.98564730), 360.0);	// h
      shpn[2] = deg2rad * fmod(274.350 + (td *  0.11140410), 360.0);	// p
      shpn[3] = deg2rad * fmod(343.510 + (td *  0.05295390), 360.0);	// n
      shpn[4] = deg2rad * fmod(283.000 + (td *  0.00000000), 360.0);	// p1

      //Shailen's degrees per Julian day(solar?).
      b[2] =  13.17639648;
      b[3] =   0.98564736;
      b[1] = 360.0 - b[2] + b[3];//Out of order b/c depends on 2,3- see eq. 2.26.
      b[4] =   0.11140353;
      b[5] =   0.05295377;
      b[6] =   0.00004706;
      //*/

      //   itj	Desired UTC time, in (decimal) Modified.
      //   tt		Mean solar angle relative to Greenwich
      //   hp		Mean solar longitude
      //   s		Mean lunar longitude
      //   p1		Longitude of solar perigee
      //   p		Longitude of lunar perigee</param>

      //Mean lunar time (UNITS?!??).
      doodson.tau=360;
      doodson.args.at(1) = doodson.tau;

      //Mean lunar lon (deg).
      doodson.s = astro.s;
      doodson.args.at(2) = doodson.s;

      //Mean solar lon (deg).
      doodson.h = astro.hp;
      doodson.args.at(3) = doodson.h;

      //Mean lunar perigee lon (deg).
      doodson.p = astro.p;
      doodson.args.at(4) = doodson.p;

      //Negative mean lon of ascending lunar node (deg).
      //WAIT.. WHY IS HP SOLAR LON *AND* LON OF ASC. LUNAR NODE?
      doodson.np =360;
      doodson.args.at(5) = doodson.np;

      //Mean solar perigee lon (deg).
      doodson.ps = astro.p1;
      doodson.args.at(6) = doodson.ps;

      //Convert these particular b[]'s into rad/sec.
      //cout<<"FES can't calculate omegas!"<<endl;
      for(i=1;i<(int)doodson.omegas.size();i++){
        doodson.omegas.at(i) = b[i]*omega_conversion;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 3:{//Values from Wenzel "Tide-Generating Potential for the Earth"
            //in table 4, Simon et al 1994

      //This is the epoch used for my Doodson argument calculation.
      //Relative to J2000 too!
      long long doodson_epoch = 0;

      //Julian millennia since doodson_epoch.
      double mi   = (doodson.time - doodson_epoch)*sec2jcy/10.0;
      double mi2  = mi*mi;
      double mi3  = mi2*mi;
      double mi4  = mi3*mi;

      //Values from Wenzel "Tide-Generating Potential for the Earth"
      //in table 4, Simon et al 1994
      //As usual, index 0 is empty.
      double a[7],b[7],c[7],d[7],f[7];

      //Degrees.
      a[1]   = 242.14980452999;
      a[2]   = 218.31664562999;
      a[3]   = 280.46645016002;
      a[4]   =  83.35324311998;
      a[5]   = 234.95544499000;
      a[6]   = 282.93734098001;

      //Degrees per Julian millenia.
      b[1] =  127037328.88553056;
      b[2] =    4812678.81195750;
      b[3] =     360007.69748806;
      b[4] =      40690.13635250;
      b[5] =      19341.36261972;
      b[6] =         17.19457667;

      //Converts from b[] units to rad/sec.
      long double omega_conversion = deg2rad*(sec2jcy/10.0);

      //Degrees per Julian millenia^2.
      c[1]     =   0.17696111;
      c[2]     =  -0.14663889;
      c[3]     =   0.03032222;
      c[4]     =  -1.03217222;
      c[5]     =  -0.20756111;
      c[6]     =   0.04568889;

      //Degrees per Julian millenia^3.
      d[1]     =  -0.00183140;
      d[2]     =   0.00185140;
      d[3]     =   0.00002000;
      d[4]     =  -0.01249168;
      d[5]     =  -0.00213942;
      d[6]     =  -0.00001776;

      //Degrees per Julian millenia^4.
      f[1]     =   0.00008824;
      f[2]     =  -0.00015355;
      f[3]     =  -0.00006532;
      f[4]     =   0.00052655;
      f[5]     =   0.00016501;
      f[6]     =  -0.00003323;

      //Mean lunar time (UNITS?!??).
      i=1;
      doodson.tau=restrict_degrees(a[i]+b[i]*mi+c[i]*mi2+d[i]*mi3+f[i]*mi4);
      doodson.args.at(i) = doodson.tau;

      //Mean lunar lon (deg).
      i=2;
      doodson.s=restrict_degrees(a[i]+b[i]*mi+c[i]*mi2+d[i]*mi3+f[i]*mi4);
      doodson.args.at(i) = doodson.s;

      //Mean solar lon (deg).
      i=3;
      doodson.h=restrict_degrees(a[i]+b[i]*mi+c[i]*mi2+d[i]*mi3+f[i]*mi4);
      doodson.args.at(i) = doodson.h;

      //Mean lunar perigee lon (deg).
      i=4;
      doodson.p=restrict_degrees(a[i]+b[i]*mi+c[i]*mi2+d[i]*mi3+f[i]*mi4);
      doodson.args.at(i) = doodson.p;

      //Negative mean lon of ascending lunar node (deg).
      i=5;
      doodson.np=restrict_degrees(a[i]+b[i]*mi+c[i]*mi2+d[i]*mi3+f[i]*mi4);
      doodson.args.at(i) = doodson.np;

      //Mean solar perigee lon (deg).
      i=6;
      doodson.ps=restrict_degrees(a[i]+b[i]*mi+c[i]*mi2+d[i]*mi3+f[i]*mi4);
      doodson.args.at(i) = doodson.ps;

      //Convert these particular b[]'s into rad/sec.
      for(i=1;i<(int)doodson.omegas.size();i++){
        doodson.omegas.at(i) = b[i]*omega_conversion;
      }

      break;
    }
  }//End of switch-case.

  //Get Doodson numbers for this period.
  period_info_s period_info;
  is_this_period_recognized(doodson.input_period, period_info);
  //Copy only the parameters which have changed to avoid over-writing!
  doodson.numbers = period_info.doodson.numbers;
  doodson.h2kj_sign = period_info.doodson.h2kj_sign;

  double omega = 0.0;
  for(i=1;i<(int)doodson.omegas.size();i++){
    omega += doodson.numbers.at(i)*doodson.omegas.at(i);
  }
  doodson.output_period = twoPi/(omega);//In seconds.

  //If defined and Doodson phases are off by >1 sec, alert!
  if(fabs(doodson.input_period - doodson.output_period) > 1) cout<<"WARNING!!! Doodson period diff for "<<period_info.title<<" (seconds): "<<setprecision(10)<<setw(15)<<doodson.input_period - doodson.output_period<<endl;
  
  //Now that times, Doodson args and nums, and periods are calculated,
  //calculate the Greenwich phase at this time for this tidal constituent.

  //Delta function in eq. 2.31 in Shailen's thesis.
  int delta1;//This calculation will have to be upgraded with n>2!
  if(doodson.numbers[1]==1) delta1 = 1;//Diurnals.
  else delta1 = 0;//Semi-diurnals and long period tides.

  //Delta function in eq. 4.1,2 in Shailen's thesis.
  int delta2 = 1;
  //First, the Kronecker delta in eq. 4.2.
  if(doodson.numbers[1]==0) delta2 *= -1;
  //Next, the N2k normalization factor in eq. 4.2 and 2.20.
  delta2 *= (int)pow(-1,doodson.numbers[1]);
  //THEN, H2kj's sign from table 2.2
  delta2 *= doodson.h2kj_sign;
  //Finally, 1 if delta2<0, 0 if delta2>0.
  if(delta2<0) delta2 = 1;
  else delta2 = 0;

  doodson.phase = restrict_degrees(doodson.numbers[1]*doodson.tau + doodson.numbers[2]*doodson.s + doodson.numbers[3]*doodson.h + doodson.numbers[4]*doodson.p + doodson.numbers[5]*doodson.np + doodson.numbers[6]*doodson.ps - delta1*90.0 + delta2*180.0);
  cout<<"Greenwich phase for "<<period_info.title<<" is "<<doodson.phase<<" degrees."<<endl;
}

void adjust_phases(input_s &input, results_s &results){
  /**********************************************************************
  Purpose:  In order to decouple the constant and trend terms, and to
            improve the accuracy of cos(w*t) terms, the input times have
            had "input.epoch" subtracted from them, where input.epoch is
            currently the average of the first and last input times.

            This means the phases are relative to this epoch, which is
            not the J2000 epoch. The phases are adjusted here.

            Also, this function can change the Greenwich phase lags to
            J2000 phases.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k;
  double phase_offset;
  doodson_s doodson;
  doodson.type=3;//Simon 1994.

  //This function controlled by "adjust_phases_choice" in results.options.
  //-1  - Disabled.
  //1   - Convert from J2000 epoch to Greenwich phase lags.
  //2   - Convert from Greenwich phase lags to J2000 epoch.
  //3   - Convert from input.epoch to J2000 epoch
  //4   - Convert from J2000 epoch to input.epoch
  if(results.options.adjust_phases_choice != -1) cout<<"Adjusting phases using adjust_phases_choice "<<results.options.adjust_phases_choice<<endl;
  else return;

  //Make sure that current_phase_format matches adjust_phases_choice.
  //If not, complain.
  switch(results.options.adjust_phases_choice){
    default: cout<<"!!!!WARNING!!!!!! adjust_phases_choice "<<results.options.adjust_phases_choice<<" isn't recognized."<<endl;
    case 1://All of these should have current_phase_format == choice!
    case 2:
    case 3:{
      if(results.options.current_phase_format != results.options.adjust_phases_choice) cout<<"!!!WARNING!!! current_phase_format = "<<results.options.current_phase_format<<", but adjust_phases_choice = "<< results.options.adjust_phases_choice<<endl;
      break;
    }
    case 4:{//This choice requires input in J2000 format.
      if(results.options.current_phase_format != 1) cout<<"!!!WARNING!!! current_phase_format = "<<results.options.current_phase_format<<", but adjust_phases_choice = "<< results.options.adjust_phases_choice<<endl;
      break;
    }
  }//End of switch-case.

  //If sin/cos results, temporarily convert to amp/phase.
  convert_results_from_sincos_to_ampphase(results);

  k=0;//Holds current parameter index.
  //Loop thru outputtypes (periods count twice)- only affect amp/phases.
  for(j=0;j<(long long)results.options.output_type.size();j++){
    //New output types should be added here.
    if(results.options.output_type.at(j)==101 or results.options.output_type.at(j)==102){
      //I'm assuming that if ANY sines,cosines are found,
      //that there are NO amps or phases.
      //This shouldn't be needed b/c of convert_results...() above.
      cout<<"adjust_phases() is supposed to be fed only amps/phases, but results.options.output_type["<<j<<"] = "<<results.options.output_type.at(j)<<" which is either an sine(2) or a cosine(3). Now leaving this function without doing anything."<<endl;
      return;
    }
    else if(results.options.output_type.at(j)==104){//Phase detected.
      //This MUST be a period param!
      if(results.options.parameter_type.at(k) != 101)
        cout<<"!!!!WARNING!!! This MUST be a period, but results.options.parameter_type["<<k<<"] = "<<results.options.parameter_type.at(k)<<endl;

      //Set phase_offset differerently based on phase_choice.
      //But always initialize to zero in case none of these activate!
      phase_offset=0.0;
      switch(results.options.adjust_phases_choice){
        default: cout<<"!!!!WARNING!!!!!! adjust_phases_choice "<<results.options.adjust_phases_choice<<" isn't recognized."<<endl;
        case 1://Exactly same as 2 but in reverse.
        case 2:{//Convert from Greenwich phase lags to J2000 phases.

          //Only adjust tide phases.
          if(results.options.parameter_tide.at(k) == 1){
            doodson.time = 0;
            doodson.input_period=results.options.parameter.at(k);
            greenwich_phase_at_epoch(doodson);
            phase_offset = -doodson.phase;
          }

          //Treat cases 1,2 differently.
          if(results.options.adjust_phases_choice == 1){
            phase_offset *= -1;
            results.options.current_phase_format = 2;
          }
          else if(results.options.adjust_phases_choice == 2){
            results.options.current_phase_format = 1;
          }

          break;
        }
        case 3:{//Convert from input.epoch to J2000 epoch
          //Difference between current input's epoch time, which is
          //relative to J2000 (in GPS seconds), and J2000.
          //This time difference results in a phase offset (in degrees):
          phase_offset = (double)(restrict_degrees(input.epoch/(results.options.parameter.at(k))*360.0));
          results.options.current_phase_format = 1;

          break;
        }
        case 4:{//Convert from J2000 to input.epoch
          //Difference between current input's epoch time, which is
          //relative to J2000 (in GPS seconds), and J2000.
          //This time difference results in a phase offset (in degrees):
          phase_offset = (double)(restrict_degrees(-input.epoch/(results.options.parameter.at(k))*360.0));
          results.options.current_phase_format = 3;

          break;
        }
      }//End of switch-case.

      //Loop through grid points to change phases at all grid points.
      for(i=0;i<(long long)results.outputs.at(j).size();i++){
        results.outputs.at(j).at(i) = restrict_degrees(results.outputs.at(j).at(i) + phase_offset);

      }//End of loop through grid points.
      k++;//Move on to next parameter.
    }//End of "if this is a phase, adjust it".
    //New output types should be added here.
    else if(results.options.output_type.at(j)==103){//Amp detected.
      //Do nothing- don't advance k so the phase uses this parameter.
    }
    else if(results.options.output_type.at(j)<100){//Polynomial detected.
      k++;//Move on to next parameter.
    }
    else if(results.options.output_type.at(j) >= 10000 and results.options.output_type.at(j) < 20000){//Arbitrary detected.
      k++;//Move on to next parameter.
    }
    else cout<<"Output type "<<results.options.output_type.at(j)<<" was not recognized."<<endl;
  }//End of loop through output_type.
  
  //If needed, convert amp/phase back to sin/cos.
  if(results.conversion_to_ampphase==2) convert_results_from_ampphase_to_sincos(results);
}

h_matrix_s create_h_matrix(input_s &input, grid_s &grid, results_s &results){
  /**********************************************************************
  Purpose: This function creates the H matrix (as in y=Hx+e).
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  h_matrix_s h_matrix;
  h_matrix.stop_uninitialized_warning = 1;//Does nada but stops g++ warning
  //Requires GSL libraries.
  #ifdef GSL_HERE
  long long i,j,k;
  vector<double> sep_vec;//Unit vector - points from GRACE A to B.
  sep_vec.resize(3,0.0);
  double distanceAB;//between GRACE A and B.
  vector<double> grid2A;//vector that points from grid pt to GRACE A.
  grid2A.resize(3,0.0);
  vector<double> grid2B;//vector that points from grid pt to GRACE B.
  grid2B.resize(3,0.0);
  double distanceA,distanceB;//between grid pt and GRACE A, GRACE B.
  double da_dmj;//deriv of along-track acc w.r.t. mascon j's (current) mass.
  long long column;//Counter which is reset after each row is filled.

  //Initialize variables for notification function for time remaining.
  notification_s notification;
  //1st notification occurs after two loops, but it usually won't print
  //anything because not enough total_time will have passed (first_interval).
  //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
  notification.interval=2;
  //Desired min and max intervals in seconds b/w notifications.
  notification.first_interval=10;
  notification.min_interval=8*3600, notification.max_interval=12*3600;
  notification.start_time=-1; notification.current_time=-1;
  notification.previous_time=-1;//Needs to be initialized
  notification.prefix = results.options.prefix;

  //Disable partial save code here, but init the new variables.
  notification.starting_index = 0;
  notification.partial_saves_enabled = 0;
  //Used to size notification intervals.
  notification.previous_i=notification.starting_index;
  notification.num_loops = input.time.size()-notification.starting_index;
  //notification.interval will not be allowed to go above this number
  //so that at LEAST a certain number of messages are seen.
  notification.max_notif_interval=(long long)(notification.num_loops/4);
  //Dummy variables for notify().
  least_squares_s dummy_ls;

  switch(results.options.h_matrix_choice){
    default: cout<<"!!!!WARNING!!!!!! results.options.h_matrix_choice "<<results.options.h_matrix_choice<<" isn't recognized."<<endl;
    case 1:{//GSL format-pt. masses w/ all params for grid pt 1, then all
            //params (sine, cos, trend, const, etc) for grid pt 2, etc.
      
      cout<<"!!!WARNING!!! CREATE_H_MATRIX HAS NOT BEEN UPGRADED TO GENERALIZED POLYNOMIALS OR ARBITRARY FUNCTIONS, ETC, YET!"<<endl;

      //Initialize h_matrix- correct dimensions and populated with 0.0.
      h_matrix.gsl = gsl_matrix_alloc(input.time.size(), (long long)grid.lat.size()*results.options.output_type.size());
      gsl_matrix_set_zero(h_matrix.gsl);

      //Populate h_matrix:
      cout<<results.options.prefix<<" is populating H matrix..."<<endl;
      //Loop through input points- this is the outer loop because
      //the sep_vec- vector between the satellites, is calculated
      //only once per input time this way.
      notification.start_time = time(NULL);
      for(i=notification.starting_index;i<(long long)input.time.size();i++){

        notify(notification,i,dummy_ls,input,results);//Periodically print time remaining.

        //Because the H matrix produces acceleration in nm, need to scale!
        input.accel.at(i) *= 1E9;

        //Each loop is a row (const input time)-column resets on each loop
        column=0;

        //Calculate sep_vec for this time.
        sep_vec.front() = input.xB.at(i) - input.xA.at(i);
        sep_vec.at(1) = input.yB.at(i) - input.yA.at(i);
        sep_vec.at(2) = input.zB.at(i) - input.zA.at(i);

        //Normalize sep_vec.
        distanceAB = sqrt(pow(sep_vec.front(),2)+pow(sep_vec.at(1),2)+pow(sep_vec.at(2),2));
        sep_vec.front() = sep_vec.front()/distanceAB;
        sep_vec.at(1) = sep_vec.at(1)/distanceAB;
        sep_vec.at(2) = sep_vec.at(2)/distanceAB;
        //After normalizing, convert distanceAB from km to m.
        distanceAB = distanceAB*1000.0;

        //Loop through grid- this is the next loop because this way
        //the distance from the input point(s) to each grid point is only
        //calculated once for each satellite.
        for(j=0;j<(long long)grid.lat.size();j++){
          //Calculate vector/distance from grid point to input point A,B.
          //Convert from km to m.
          grid2A.front() = 1000.0*(input.xA.at(i) - grid.x.at(j));
          grid2A.at(1) = 1000.0*(input.yA.at(i) - grid.y.at(j));
          grid2A.at(2) = 1000.0*(input.zA.at(i) - grid.z.at(j));
          grid2B.front() = 1000.0*(input.xB.at(i) - grid.x.at(j));
          grid2B.at(1) = 1000.0*(input.yB.at(i) - grid.y.at(j));
          grid2B.at(2) = 1000.0*(input.zB.at(i) - grid.z.at(j));

          //Also calculate distances.  Already converted from km to m.
          distanceA = sqrt(pow(grid2A.front(),2)+pow(grid2A.at(1),2)+pow(grid2A.at(2),2));
          distanceB = sqrt(pow(grid2B.front(),2)+pow(grid2B.at(1),2)+pow(grid2B.at(2),2));

          //Now deriv of along-track acc w.r.t. mascon j's (current) mass.
          //A-B instead of B-A as on p81 of notes bc of direction of
          //grid2A/B vs deriv-induced neg sign.
          da_dmj = Big_G*1E9*grid.cm2kg.at(j)*((grid2A.front()*sep_vec.front()+grid2A.at(1)*sep_vec.at(1)+grid2A.at(2)*sep_vec.at(2))/pow(distanceA,3) - (grid2B.front()*sep_vec.front()+grid2B.at(1)*sep_vec.at(1)+grid2B.at(2)*sep_vec.at(2))/pow(distanceB,3));

          //Now loop through all params, const, trend, sine, cosine..
          for(k=0;k<(long long)results.options.parameter.size();k++){
            //Need switch-case to deal with different parameter_types.
            //New parameter types should be added here.
            switch(results.options.parameter_type.at(k)){
              default: cout<<"!!!!WARNING!!!!!! results.options.parameter_type.at(k) "<<results.options.parameter_type.at(k)<<" isn't recognized."<<endl;
              case 0:{//Constant term.
                gsl_matrix_set (h_matrix.gsl, i, column,da_dmj);
                column++;
                break;
              }
              case 1:{//Secular trend.
                gsl_matrix_set (h_matrix.gsl, i, column, da_dmj*(input.time.at(i))/seconds_in_year);
                column++;
                break;
              }
              case 101:{//Harmonic term.
                gsl_matrix_set (h_matrix.gsl, i, column, da_dmj*cos(twoPi/results.options.parameter.at(k)*(input.time.at(i))));
                gsl_matrix_set (h_matrix.gsl, i, column+1, da_dmj*sin(twoPi/results.options.parameter.at(k)*(input.time.at(i))));
                column+=2;
                break;
              }
            }//End of switch-case.
          }//End of loop through parameters
        }//End of loop through grid points
      }//End of loop through input
      break;
    }
  }//End of switch-case
  #endif
  return h_matrix;
}

void least_squares_cache_initialize(grid_s &grid,
                                    grid_s &region,
                                    input_s &input,
                                    results_s &results){
  /**********************************************************************
  Purpose: This function searches the least squares cachefolder for a
          subfolder that matches the current grid and region options, as
          well as the current parameter list and input filename.

          There are two types of matches.  Either the currently desired
          parameter list is an exact match of the parameter list in the
          cache, or they're a subset or re-ordered set of the cache
          parameters.  An exact match is easy- just load the A,b matrices.
          Loading a subset or a reordered set requires creating a vector
          of ls_indices that tells create_least_squares() how to load and
          trim (or reorder) the cache matrices.

          If there are NO matches, this function creates a foldername in
          which to save the matrices.  The foldername is stored in
          results.options and passed to every instance of
          create_least_squares - this insures that every instance saves
          to precisely the same folder, even though the foldername includes
          the current system time, which means that if the foldername was
          generated on-the-fly, it would be different for each region.

          Note that the matrix files only contain the part of the A matrix
          on top of (and including) the diagonal.  This is because the A
          matrix is symmetric across the diagonal, so there's no reason
          to store the whole thing.

          This function has grid subset matching disabled, but it might
          be useful someday as noted below if I ever figure out how to
          build the A matrix made out of input pts that are outside the
          small overlap but inside the big overlap, then subtract that
          from the loaded A matrix. That will avoid the "external input
          point contamination" problem. BUT, SINCE REG OVLP OFTEN ==
          ZONE OF INF RAD, THAT'S *ANOTHER* PROB! DELETE GRID SUBSETS!!!
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Used to search least squares cache:
  long long i,j,k;
  string foldername, current_foldername,true_foldername,desired_foldername,absolute_folder;
  DIR *cacheDIR,*cachesubDIR;
  struct dirent *cacheDirEnt,*cachesubDirEnt;
  string newfile;
  char s[max_length];

  //Used to load the options.txt file from a (potentially) matching
  //subfolder.
  results_s temp_results;
  ancillary_s temp_ancillary;
  grid_s temp_grid;
  string input_file;
  input_s temp_input;
  grid_options_s temp_region_options;
  model_options_s temp_model_options;
  plot_options_s temp_plot_options;
  insitu_s temp_insitu;//For tide gauge data, etc.

  //Used to create a new subfolder if necessary.
  FILE *output;
  string temp_string;

  istringstream stream1;//Used to grab data from strings.

  //Used to look for the subfolder with the most files that were requested regions.
  vector <string> temp_filenames;
  long long largest_file_count = -10000;

  //If this is a matching subset, this variable is set to 1.  It's
  //immediately reset to 0 after the code that compares MATCHING file count to any
  //other matches (of any kind - subset or exact) to see if it should be
  //recorded as the best possible folder to load from.
  int matching_subset = 0;
  vector <long long> desired_parameter_found(results.options.parameter.size(),0);
  //Holds copy of what may become a_o.least_squares_parameter_indices;
  vector<long long> temp_indices;

  int found_matching_folder=0;

  //Need to copy this stuff from options.txt to results.options so that
  //the matrices can be rebuilt using the subset code.
  long long temp1;//holds output_type.size()
  vector<int> temp_param_type;//need this as well as output_type.size() to rm params

  //Length of time string, used to strip time out of folder names.
  int time_size=16;

  //Region overlap position and size in the subfolder name, for removal.
  //(Used in grid subsets which currently aren't activated- see below.)
  int ropos=119,rosize=8;
  //Grid topo type position and size in the subfolder name, for removal.
  //(Topo type changes grid, but doesn't alter least squares matrices!)
  int topopos=14,toposize=2;
  string desired_topotype,cached_topotype;
  //These variables are legacy now- I've abandoned the idea of grid subsets
  int grid_subset;//Are current matrices a grid subset of cached matrices?
  string cached_region_overlap,cached_foldername_no_overlap;
  string desired_region_overlap,desired_foldername_no_overlap;

  //Used to load the verification_points vector from the cache.
  FILE *svp_fp;
  string input_verification_points_file;
  vector<long long> old_verification_points;
  vector<string>::iterator isvp;//Used to search temp_filenames for svpf.
  long long svpi;//Contains isvp converted to int index to access temp_filenames
  long long utemp;

  char* f_return;//Used to stop fgets return value warning.

  //First thing to do is initialize cache_load as "don't touch the cache"
  //so that if this variable for some reason isn't assigned, it's
  //set to a safe setting by default.
  results.options.least_squares_cache_load = 0;

  //Now define features of this h matrix choice:
  results.options.already_moved_to_cache = 0;//Default.
  switch(results.options.h_matrix_choice){
    default: cout<<"!!!!WARNING!!!!!! results.options.h_matrix_choice "<<results.options.h_matrix_choice<<" isn't recognized."<<endl;
    case 101:
    case 1:{
      results.options.uses_nmax        = 0;
      //Types 1,101 only use disk_radius if used in cm2kg in define_grid.
      if(grid.options.area_type==0) results.options.uses_disk_radius = 0;
      else results.options.uses_disk_radius = 1;
      results.options.uses_support_grid= 0;
      break;
    }
    case 2:{
      results.options.uses_nmax        = 1;
      results.options.uses_disk_radius = 1;
      results.options.uses_support_grid= 0;
      break;
    }
    case 202:
    case 201:{
      results.options.uses_nmax        = 0;
      //Type 201 only uses disk_radius if used in cm2kg in define_grid.
      if(grid.options.area_type==0) results.options.uses_disk_radius = 0;
      else results.options.uses_disk_radius = 1;
      results.options.uses_support_grid= 1;
      if(results.options.h_matrix_choice==202)
        results.options.already_moved_to_cache = 1;

      break;
    }
  }

  //Custom grids should use a special subfolder that's auto-deleted.
  desired_foldername = CUSTOM_GRID_LS_SUBFOLDER;
  absolute_folder=results.options.least_squares_cachefolder;
  absolute_folder.append(desired_foldername);
  remove_folder(absolute_folder);
  if(grid.options.type == 0){
    results.options.least_squares_cache_load = 0;//Just to be absolutely sure code block after goto runs.
    goto end_of_least_squares_cache_initialize;
  }

  //Next, figure out exactly what folder we're looking for by
  //creating a filename that corresponds to currently selected options.
  desired_foldername=create_least_squares_cache_foldername(region.options,grid.options,results);
  //cout<<"desired_foldername: "<<desired_foldername<<endl;

  //Search for previously computed matrices in the cache, but only if
  //noload == 0.
  //Each unique set of options creates a folder with a folder name chosen
  //by create_least_squares_cache_foldername(). Many different files are
  //contained within each folder- the number being greater than the num of
  //regions specified in region.options. Each file contains the A and b
  //elements of the least squares calculation, along with a couple of other
  //odds and ends that this function normally calculates from scratch.
  //options.txt also stores options, input_verif_points.txt is used to
  //set aside some input data from inversion and only use it to verify the result.
  //Also, partial_save_details_region_XXXXXX.txt files allow for incomplete saves.
  if(results.options.least_squares_noload == 0){

    cout<<"Looking for least squares cache subfolder that matches currently requested input, grid, region and analysis options..."<<endl;
    //Actual folder names have the creation time prepended to prevent
    //identical folder names.  Strip time off now for comparison.
    foldername = desired_foldername;
    foldername.erase(0,time_size);
    //Now remove topo type.
    desired_topotype = foldername.substr(topopos,toposize);
    foldername.erase(topopos,toposize);
    //cout<<"desired topotype: "<<desired_topotype<<endl;
    //cout<<"desired_foldername (no time or topo type): "<<foldername<<endl;

    //Open the main cache folder.
    cacheDIR = opendir(results.options.least_squares_cachefolder.c_str());

    //Examine each main folder, see if it contains the desired subfolder.
    //If a match is found, find out how many completed regions it contains,
    //then at the end use the one with the most completed cache files.
    cacheDirEnt = readdir(cacheDIR);
    while(cacheDirEnt != NULL){
      strcpy(s,cacheDirEnt->d_name);
      //Contains the ACTUAL foldername, with the time appended.
      true_foldername = s;
      //Skip this "folder" if its length is below 3 (i.e. it's .. or .)
      if((int)true_foldername.length() < 3) goto end_of_subfolder_loop;

      //cout<<"candidate true_foldername (current w/ time): "<<true_foldername<<endl;
      //Actual folder names have the creation time appended to prevent
      //identical folder names.  Strip time off now for comparison.
      current_foldername = true_foldername;
      current_foldername.erase(0,time_size);
      //Now remove topo type.
      cached_topotype = current_foldername.substr(topopos,toposize);
      current_foldername.erase(topopos,toposize);
      //cout<<"cached topotype: "<<cached_topotype<<endl;
      //cout<<"current_foldername (no time or topo type): "<<current_foldername<<endl;

      //Also, folders have details about region overlap distance.  It's
      //possible to load a matrix with all grid,region details identical
      //except the overlap_distance, which must be
      //smaller in the desired options than in the cached options.
      //But first, I need to extract the region options from both
      //foldernames, then save all four resulting strings.
      cached_region_overlap = current_foldername.substr(ropos,rosize);
      desired_region_overlap = foldername.substr(ropos,rosize);

      //cout<<"cached_region_overlap: "<<cached_region_overlap<<endl;
      //cout<<"desired_region_overlap: "<<desired_region_overlap<<endl;

      cached_foldername_no_overlap = current_foldername;
      desired_foldername_no_overlap = foldername;

      cached_foldername_no_overlap.erase(ropos,rosize);
      desired_foldername_no_overlap.erase(ropos,rosize);

      //cout<<"cached_foldername_no_overlap:  "<<cached_foldername_no_overlap<<endl;
      //cout<<"desired_foldername_no_overlap: "<<desired_foldername_no_overlap<<endl;

      //If the desired and cached overlaps are identical, this isn't a grid
      //subset and it can be loaded as normal.  Set grid_subset = 0.
      //If the desired overlap is less than cache overlap, this folder can
      //be loaded as a grid subset.  Set grid_subset = 1.
      //If the desired overlap is greater than the cache overlap, this
      //folder cannot be loaded.  Set grid_subset = -1 which will
      //have the same effect as if these two foldernames didn't match.
      //NOW I can tell if this is a grid subset or not.
      //UPDATE - Grid subsets include input points from outside the region
      //requested, so I'm turning them off by not allowing grid_subset=1.
      if(desired_region_overlap == cached_region_overlap) grid_subset = 0;
      else if(desired_region_overlap < cached_region_overlap) grid_subset=-1;
      else grid_subset = -1;

      //cout<<"grid_subset: "<<grid_subset<<endl;

      //If this foldername matches name, look at options.txt for params.
      //Notice I'm comparing foldernames without region overlap distances.
      if(grid_subset != -1 and desired_foldername_no_overlap == cached_foldername_no_overlap){
        //cout<<"foldername matches!"<<endl;
        //Go ahead and build a list of files (name>2 chars) in this folder.
        temp_filenames.clear();
        absolute_folder=results.options.least_squares_cachefolder.c_str();
        absolute_folder.append(true_foldername);
        cachesubDIR = opendir(absolute_folder.c_str());
        cachesubDirEnt = readdir(cachesubDIR);
        while(cachesubDirEnt != NULL){
          strcpy(s,cachesubDirEnt->d_name);
          temp_string = s;
          //Only include filenames if they're not "." or ".."
          if((int)temp_string.length() > 2) temp_filenames.push_back(s);
          cachesubDirEnt = readdir(cachesubDIR);
        }//End of while loop through files

        //Close the subfolder.
        closedir(cachesubDIR);

        //Sort the files alphabetically.
        sort(temp_filenames.begin(),temp_filenames.end());

        //Read options file in candidate subfolder.
        input_file = absolute_folder;
        input_file.append("/options.txt");
        load_options(input_file, temp_input, temp_region_options, temp_ancillary, temp_grid, temp_model_options, temp_results, temp_plot_options, temp_insitu,0);

        //Record output_type.size(), param_type for this folder temporarily.
        temp1 = temp_results.options.output_type.size();
        temp_param_type = temp_results.options.parameter_type;

        //Next, examine files in this subflder to see how many rg files it
        //contains relative to the list of requested regions.
        //Search temp_filenames for p_s_details_region_XXXXXX.txt files,
        //and modify the ps.* variables in temp_results.options.
        //(First, need to copy some info into temp_results.options.)
        temp_results.options.least_squares_cachesubfolder = true_foldername;
        temp_results.options.least_squares_cache_filenames = temp_filenames;
        check_ls_subfolder_completeness(temp_results,region);

        //If the zone of influence doesn't match the desired zone, goto
        //next folder.
        if(temp_results.options.zone_of_influence_choice != results.options.zone_of_influence_choice) goto end_of_subfolder_loop;

        //Now test the requested grid area_type to see if it's the same as
        //the cached grid area_type.
        if(grid.options.area_type != temp_grid.options.area_type)
          goto end_of_subfolder_loop;

        //Only compare radii if necessary. Note that I'm assuming only type
        //0 doesn't use the radii. (type 0 zone of influence, not hmc!)
        if(temp_results.options.zone_radius != results.options.zone_radius and results.options.zone_of_influence_choice != 0) goto end_of_subfolder_loop;

        //Now if (and ONLY if) this hmchoice uses disk_radius and nmax,
        //test those values to see if they're the same!
        if(results.options.uses_nmax == 1 and results.options.nmax != temp_results.options.nmax) goto end_of_subfolder_loop;

        if(results.options.uses_disk_radius == 1 and results.options.disk_radius != temp_results.options.disk_radius) goto end_of_subfolder_loop;

        //Now if (and ONLY if) this hmchoice uses supporting grid code,
        //test the requested support grid to see if it's the same as the
        //cached support grid code.
        if(results.options.uses_support_grid == 1)
          if(grid.options.support_type != temp_grid.options.support_type or grid.options.support_lat_multiplier != temp_grid.options.support_lat_multiplier or grid.options.support_lon_multiplier != temp_grid.options.support_lon_multiplier) goto end_of_subfolder_loop;

        //Load file that contains input verification_points vector from
        //the run that produced this folder (if that file is listed in
        //temp_filenames).

        //Search temp_filenames for "input_verification_points.txt"
        svpi = -1;
        isvp = find(temp_filenames.begin(),temp_filenames.end(),"input_verification_points.txt");
        if(isvp != temp_filenames.end()) svpi = (long long)(isvp - temp_filenames.begin());

        if(svpi >= 0){//If svp file was found in temp_filenames.
          input_verification_points_file = absolute_folder;
          input_verification_points_file.append("/");
          input_verification_points_file.append(temp_filenames.at(svpi));
          svp_fp = fopen(input_verification_points_file.c_str(),"r");
          if(!svp_fp) cout<<"The input verification points file, "<<input_verification_points_file<<", was in temp_filenames at index "<<svpi<<" but couldn't be opened!"<<endl;

          //Read old_verification_points vector from file, close svp_fp.
          old_verification_points.clear();
          //Load the first line which contains number of points.
          f_return=fgets(s,max_length,svp_fp);
          sscanf(s,"%lld", &j);
          //Use # of points to resize vector.
          old_verification_points.resize(j);
          //Read in old_verification_points
          for(j=0;j<(long long)old_verification_points.size();j++){
            f_return=fgets(s,max_length,svp_fp);
            sscanf(s,"%lld", &utemp);
            old_verification_points.at(j) = utemp;
          }
          fclose(svp_fp);

        }//End of input verification points filename found in temp_filenames
        else{//If svp filename not found.
          cout<<"!!!WARNING!!! The input verification points file wasn't found- the subfolder must be old: "<<absolute_folder<<" .   Setting old_verification_points vector to a zeroed copy of binindices."<<endl;
          old_verification_points.clear();
          old_verification_points.resize(input.binindices.size(),0);
        }//End of input verification points file not found.

        //Make sure the input filenames match, and make sure that the same
        //method of separating verification input points from input points
        //used in the inversion process was used to make this subfolder
        //as the method being requested now.
        //cout<<"input.options.filename      = "<<input.options.filename<<endl;
        //cout<<"temp_input_options.filename = "<<temp_input_options.filename<<endl;
        //cout<<"input.verification_points.size() = "<<input.verification_points.size()<<endl;
        //cout<<"old_verification_points.size() = "<<old_verification_points.size()<<endl;
        //for(k=0;k<(long long)input.verification_points.size();k++)
        //  if(input.verification_points.at(k) != old_verification_points.at(k))
        //    cout<<"input.verification_points["<<k<<"] = "<<input.verification_points.at(k)<<endl<<"old_verification_points["<<k<<"] = "<<old_verification_points.at(k)<<endl;

        if(input.options.filename == temp_input.options.filename and input.verification_points == old_verification_points){
          //cout<<"input filenames match!"<<endl;

          //Before comparing parameters, set all parameters in both
          //vectors to 0 if their type is != 101.  This helps to avoid
          //problems where I've accidentally given the trend a "0" as
          //a parameter instead of "1"- doesn't matter for anything except
          //sinusoids at the moment, but beware future parameter types that
          //actually use the parameter like sinusoids do!

          //New parameter types should be added here.
          //Desired parameter vector.
          for(k=0;k<(long long)results.options.parameter.size();k++) if(results.options.parameter_type.at(k) != 101) results.options.parameter.at(k) = 0.0;

          //Cached parameter vector.
          for(k=0;k<(long long)temp_results.options.parameter.size();k++) if(temp_results.options.parameter_type.at(k) != 101) temp_results.options.parameter.at(k) = 0.0;

          //The following code is for subset matches, but grid_subset may
          //turn "exact" matches into subset matches, so compute early.
          //Cycle through current parameters, make sure each one is in
          //cache parameters - but make sure it's only in cache once!
          matching_subset = 1;//now prove this isn't a matching subset!

          //Initialize vectors used in comparison and later in loading.
          desired_parameter_found.clear();
          desired_parameter_found.resize(results.options.parameter.size(),0);
          temp_indices.clear();
          temp_indices.resize(results.options.parameter.size(),0);
          for(i=0;i<(long long)results.options.parameter.size();i++){
            for(j=0;j<(long long)temp_results.options.parameter.size();j++){
              //Does i'th des. param and type match j'th cache param,type?
              //Match parameter types first.
              if(results.options.parameter_type.at(i)==temp_results.options.parameter_type.at(j)){
                //If parameter types match, see if parameters match
                //ONLY if these aren't sinusoids. (otherwise don't matter)
                //Matching parameters are any parameters that are less than
                //45 seconds apart from each other.  I chose this number
                //because m2_plus and m2_minus are probably as close to
                //m2 as any tide will reasonably be, and they're 60s apart.
                if(results.options.parameter_type.at(i) != 101 or fabs(results.options.parameter.at(i)-temp_results.options.parameter.at(j))<= 45.0){

                  desired_parameter_found.at(i)++;
                  temp_indices.at(i)=j;
                }
              }
            }//End of loop through cache parameters.
          }//End of loop through desired parameters.

          //Make sure each parameter was found only once.
          for(i=0;i<(long long)results.options.parameter.size();i++){
            if(desired_parameter_found.at(i) != 1){
              //These are old debug commands, they just take up space in
              //output and confuse matters.
              //cout<<"WARNING!!! desired_parameter_found["<<i<<"]: "<<desired_parameter_found.at(i)<<endl<<"that parameter has a_o.parameter: "<<results.options.parameter.at(i)<<" and type: "<<results.options.parameter_type.at(i)<<endl;
              //cout<<"Just for kicks, the parameter value at the same index in the cached vector is: "<<temp_results.options.parameter.at(i)<<endl;
              //cout<<"...and the type is: "<<temp_results.options.parameter_type.at(i)<<endl;
              matching_subset = 0;
            }
          }

          if(1==2){
            for(i=0;i<(long long)results.options.parameter.size();i++){
              cout<<"temp param["<<i<<"] = "<<temp_results.options.parameter.at(i)<<" param["<<i<<"] = "<<results.options.parameter.at(i)<<endl;
              cout<<"temp param type["<<i<<"] = "<<temp_results.options.parameter_type.at(i)<<" param type["<<i<<"] = "<<results.options.parameter_type.at(i)<<endl;
            }
          }

          //See if desired parameters are exactly equal to those in folder.
          if(temp_results.options.parameter == results.options.parameter and temp_results.options.parameter_type == results.options.parameter_type){
            //THIS CODE MUST BE UPGRADED TO COMPARE COMPLETION PERCENTAGES?
            //IF THERE CAN'T BE TWO EXACT MATCHES, THAT'S NOT NECESSARY!
            //(that implies this code needs to be upgraded when subset
            //matches are allowed with partial save code.)
            if(largest_file_count < temp_results.options.ps.completed_requested_regions){
              cout<<"Found an exactly matching matrix folder in cache! Folder "<<true_foldername<<" can be used to load "<<(long long)temp_results.options.ps.completed_requested_regions<<" completed files and "<<temp_results.options.ps.partial_requested_regions<<" incomplete requested region cache files."<<endl;

              largest_file_count = results.options.ps.completed_requested_regions;
              results.options.least_squares_cache_load=1;
              results.options.least_squares_cachesubfolder = true_foldername;
              results.options.least_squares_cache_filenames = temp_filenames;
              results.options.ls_cache_h_size = temp1;
              results.options.ls_cache_parameter_type = temp_param_type;
              //Next command copies the subfolder completeness information
              //from temporary struct to the permanent results.options.
              results.options.ps = temp_results.options.ps;
              //This vector was filled in check_ls_subfolder_completeness().
              results.options.least_squares_cache_region_indices = temp_results.options.least_squares_cache_region_indices;
              //Generate (hopefully load) the region that was used to
              //create this cache subfolder.  This will be most useful
              //for its center/edge grid indices if I choose to perform
              //grid subset matches by allowing the cached region to have
              //a higher overlap distance than currently desired.
              //GRID SUBSETS HAVE BEEN TURNED OFF BECAUSE UNLIKE PARAM
              //SUBSETS, GR SUBSETS INCLUDE EXTRA ST PTS!
              //THIS CAN'T BE FIXED! WELL, IT CAN BY FIGURING OUT WHICH ST
              //PTS WERE IN THE SUPERSET GRD OVERLAP AND MAKING A MATRIX
              //FROM THOSE ST PTS ONLY, THEN SUBTRACTING THOSE FROM THE
              //LOADED A,B MATRICES... hmmm......

              //Need to load ancillary for the regions!
              load_ancillary(temp_ancillary, 0);
              temp_region_options.noload = 0;//Don't care about old noload!
              results.options.ls_region = define_region(temp_region_options, grid, 0);
              results.options.least_squares_parameter_indices=temp_indices;
              results.options.grid_subset = grid_subset;
              found_matching_folder=1;

            }//End of "biggest match found so far".
          }//End of exact parameter match confirmed code.
          //If not, are desired params reordered or a subset of those in
          //cache folder? (the "if(matching_subset)" part added 09-02-12!)
          else if(matching_subset == 1){
            //Dunno why this debug cout wasn't commented out long ago.
            //cout<<"Parameters don't match exactly, looking for subset/reordered match..."<<endl;

            //If they're all accounted for, this is the subfolder to load!
            //Also only load if the number of requested files is greater
            //than 0. Also, disable subset loading if ps code wasn't
            //finished with this folder.
            if(largest_file_count < temp_results.options.ps.completed_requested_regions and temp_results.options.ps.completed_requested_regions > 0 and temp_results.options.ps.partial_requested_regions == 0){

              //for(i=0;i<(long long)results.options.parameter.size();i++) cout<<"temp_indices.at(i): "<<temp_indices.at(i)<<endl;
              largest_file_count = results.options.ps.completed_requested_regions;
              results.options.least_squares_cache_filenames = temp_filenames;
              results.options.least_squares_cache_load=2;
              results.options.least_squares_cachesubfolder = true_foldername;
              results.options.least_squares_parameter_indices=temp_indices;
              results.options.ls_cache_h_size = temp1;
              results.options.ls_cache_parameter_type = temp_param_type;
              //Next command copies the subfolder completeness information
              //from the temporary structure to the permanent results.options.
              results.options.ps = temp_results.options.ps;
              //This vector was filled in check_ls_subfolder_completeness().
              results.options.least_squares_cache_region_indices = temp_results.options.least_squares_cache_region_indices;
              //Generate (hopefully load) the region that was used to
              //create this cache subfolder.  This will be most useful
              //for its center/edge grid indices if I choose to perform
              //grid subset matches by allowing the cached region to have
              //a higher overlap distance than currently desired.
              temp_region_options.noload = 0;//Don't bother loading
              results.options.ls_region = define_region(temp_region_options, grid, 0);
              results.options.grid_subset = grid_subset;
              found_matching_folder=1;
              cout<<"Subset/reordered parameters match found. Folder "<<true_foldername<<" can be used to load "<<(long long)temp_results.options.ps.completed_requested_regions<<" requested region files."<<endl;
            }//End of subset parameter match confirmed code.
            //Again, weird that I didn't comment out this debug cout.
            //else cout<<"Subset/reordered match not found, or match has fewer filenames than previously identified match."<<endl;
          }//End of subset parameter match investigation code.
        }//End of "does this folder also have right input name?"
        //else cout<<"desired input name "<<input.options.filename<<" doesn't match cache name "<<temp_input_options.filename<<endl;
      }//End of "does this foldername match?"
      //printf("%s",cacheDirEnt->d_name); cout<<endl;
      //This label is used to allow filenames like .. or . to skip all code.
      end_of_subfolder_loop:
      cacheDirEnt = readdir(cacheDIR);
    }//End of while loop.

    //Release the open folder.
    closedir(cacheDIR);

    //If this is a grid_subset load, force use of subset loading code
    //even if the parameter list was an exact match.
    if(results.options.grid_subset == 1) results.options.least_squares_cache_load = 2;

    //Now for subset matches, need to save some info for c_l_s().
    if(results.options.least_squares_cache_load == 2){
      //Save current region to a_o so that subset loading in c_l_s() has it.
      results.options.current_region = region;
      //Also, copy ENTIRE grid to a_o.
      results.options.current_grid = grid;
    }//End of "if the loading was successful" subset match indexing code.

  }//End of "if least_squares_noload == 0"
  else cout<<"Least squares cache is not being used because results.options.least_squares_noload != 0"<<endl;

  //So that custom grids can skip to this point.
  end_of_least_squares_cache_initialize:

  //If desired_foldername not found (or noload != 0), SAVE to
  //desired_foldername instead.  (First we need to create that folder.)
  if(results.options.least_squares_cache_load == 0){

    //Create folder with desired foldername in cache folder.
    temp_string = results.options.least_squares_cachefolder;
    temp_string.append(desired_foldername);
    create_folder(temp_string);
 
    //Copy options.txt to newly-created folder.
    temp_string = "cp ";
    temp_string.append(gaiafolder());
    temp_string.append("c/options.txt ");
    temp_string.append(results.options.least_squares_cachefolder);
    temp_string.append(desired_foldername);
    //cout<<"options.txt copy string: "<<temp_string<<endl;
    output = popen(temp_string.c_str(),"r");
    pclose(output);

    results.options.least_squares_cache_load = -1;
    results.options.least_squares_cachesubfolder = desired_foldername;

    //This is a quick way to initialize the ps structure so that
    //partial saves will work correctly in this new subfolder.
    check_ls_subfolder_completeness(results,region);

    //Of course no partial save details files will be found, but the
    //partial save code should be turned on:
    results.options.ps.partial_save_code_used = 1;
  }

  //BEGINNING OF CODE MOVED FROM BUILD_ANALYSIS_REGION_LIST():
  vector<long long> temp;
  //If desired, check to make sure that each entry in i_t_a is also in
  //list of region indices that are already in the cache subfolder!
  if(results.options.least_squares_cache_load > 0 and region.options.analysis_list_cache_only == 1){
    temp.clear();
    for(i=0;i<(long long)region.indices_to_analyze.size();i++){
      if(count(results.options.least_squares_cache_region_indices.begin(),results.options.least_squares_cache_region_indices.end(),(long long)region.indices_to_analyze.at(i))==1) temp.push_back(region.indices_to_analyze.at(i));
      //else cout<<"Region index "<<region.indices_to_analyze.at(i)<<" is being skipped because it doesn't have a completed least squares cache file."<<endl;
    }
    region.indices_to_analyze = temp;

    cout<<"Removed regions that don't have completed least squares cache files.  These region indices will be analyzed:"<<endl;
    for(i=0;i<(long long)region.indices_to_analyze.size();i++)
      cout<<region.indices_to_analyze.at(i)<<", ";
    cout<<endl;
  }
  //END OF CODE MOVED FROM BUILD_ANALYSIS_REGION_LIST().

  if(results.options.least_squares_cache_load == -1) cout<<"No least squares cache folder matched parameters of current run. Populating and saving to new cache folder."<<endl;
  else if(results.options.least_squares_cache_load == 1) cout<<"Exact least squares folder match found.  Loading from cache."<<endl;
  else if(results.options.least_squares_cache_load == 2) cout<<"Parameter subset least squares folder match found.  Loading from cache."<<endl;
  else if(results.options.least_squares_cache_load == 0) cout<<"Not using least squares cache at all."<<endl;
  else cout<<"!!!WARNING!!! Didn't recognize results.options.least_squares_cache_load = "<<results.options.least_squares_cache_load<<endl;
  //cout<<"results.options.grid_subset = "<<results.options.grid_subset<<endl;
  cout<<"results.options.least_squares_cachesubfolder = "<<results.options.least_squares_cachesubfolder<<endl;
}

void read_partial_save_details_file(results_s &results){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  char s[max_length];
  long long j;//counters
  string temp_string;
  char* f_return;//Used to stop fgets return value warning.
  int temp_complete;
  long long i;

  i=results.options.requested_index;//For brevity's sake.

  //Open details file for loading.
  FILE *in_fp;
  temp_string = results.options.least_squares_cachefolder;
  temp_string.append(results.options.least_squares_cachesubfolder);
  temp_string.append("/");
  temp_string.append(results.options.ps.filenames.at(i));
  in_fp = fopen(temp_string.c_str(),"r");

  //Warn if file doesn't open correctly.
  if(in_fp == NULL) cout << "The partial save details file, "
                         << results.options.ps.filenames.at(i)
                         << ", failed to open."<<endl;

  j=0;//Counts the number of lines read.
  //Read the file line by line.
  while(fgets(s,max_length,in_fp)!=NULL){
    j+=2;//Just read the title AND value.  Not valid after unrecognized line!
    //Read odd numbered lines to determine what the next line is going
    //to be, then load it into the correct variable.

    if(!strcmp(s,"Completed? (1=complete,0=incomplete):\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%d", &temp_complete);
    }
    else if(!strcmp(s,"Total number of input points in this region:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &results.options.ps.input_numpoints.at(i));
    }
    else if(!strcmp(s,"Index of last used input point in this region:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &results.options.ps.previous_input_index.at(i));
    }
    else if(!strcmp(s,"input epoch:\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &results.options.ps.input_epoch.at(i));
    }
    else if(!strcmp(s,"input time of first/last input point in region (with epoch added back in):\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &results.options.ps.first_input_time.at(i));
      j++;//extra data line!
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &results.options.ps.last_input_time.at(i));
    }
    else if(!strcmp(s,"input time of first/last used input point in region (with epoch added back in):\n")){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &results.options.ps.first_used_input_time.at(i));
      j++;//extra data line!
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &results.options.ps.last_used_input_time.at(i));
    }
    else{
      cout<<"Did not recognize line #"<<i<<" in partial save details file: "<<s<<endl;
      j--;//Didn't read data line this time.
    }
  }
  fclose(in_fp);

  //Now use temp_complete to confirm this cache file is complete.
  if(temp_complete == 1){
    results.options.ps.initial_completeness.at(i)=3;
  }
}

void write_partial_save_details_file(results_s &results){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  FILE *new_fp;
  int complete=0;
  long long i;
  string temp_string;

  i=results.options.requested_index;//For brevity's sake.

  //Don't worry about an existing file- it's been found and moved by
  //perform_partial_save().
  temp_string = results.options.least_squares_cachefolder;
  temp_string.append(results.options.least_squares_cachesubfolder);
  temp_string.append("/");
  temp_string.append(results.options.ps.filenames.at(i));
  new_fp = fopen(temp_string.c_str(),"w");

  //There isn't actually a variable or vector for CURRENT completeness
  //(just initial completeness!). Instead, the index of the last used input
  //pt is quietly set to the num o st pts minus 1, which means "complete."
  if(results.options.ps.previous_input_index.at(i) == results.options.ps.input_numpoints.at(i)-1) complete=1;

  fprintf(new_fp,"Completed? (1=complete,0=incomplete):\n%d\n",complete);
  fprintf(new_fp,"Total number of input points in this region:\n%lld\n",results.options.ps.input_numpoints.at(i));
  fprintf(new_fp,"Index of last used input point in this region:\n%lld\n",results.options.ps.previous_input_index.at(i));
  fprintf(new_fp,"input epoch:\n%lld\n",results.options.ps.input_epoch.at(i));
  fprintf(new_fp,"input time of first/last input point in region (with epoch added back in):\n%lld\n%lld\n",results.options.ps.first_input_time.at(i),results.options.ps.last_input_time.at(i));
  fprintf(new_fp,"input time of first/last used input point in region (with epoch added back in):\n%lld\n%lld\n",results.options.ps.first_used_input_time.at(i),results.options.ps.last_used_input_time.at(i));
  fclose(new_fp);
}

void check_ls_subfolder_completeness(results_s &results, grid_s &region){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;//counters.
  istringstream stream1;//Used to grab data from strings.
  char s[max_length];

  //Need to initialize ps struct and clear old values- this is fastest!
  ps_s temp_ps;//brand new structure, so already cleared.
  results.options.ps = temp_ps;
  results.options.ps.filenames.resize(region.indices_to_analyze.size());
  results.options.ps.initial_completeness.resize(region.indices_to_analyze.size(),0);
  results.options.ps.input_numpoints.resize(region.indices_to_analyze.size(),-1);
  results.options.ps.previous_input_index.resize(region.indices_to_analyze.size(),-1);
  results.options.ps.input_epoch.resize(region.indices_to_analyze.size(),-1);
  results.options.ps.first_input_time.resize(region.indices_to_analyze.size(),-1);
  results.options.ps.last_input_time.resize(region.indices_to_analyze.size(),-1);
  results.options.ps.first_used_input_time.resize(region.indices_to_analyze.size(),-1);
  results.options.ps.last_used_input_time.resize(region.indices_to_analyze.size(),-1);

  //See which filenames are recognizably regions, and store their indices
  //(number - 1) in ls_cache_region_indices for future use.
  //Note that these indices are recorded regardless of whether or not
  //they're requested-it's used in the code moved from build_analysis_reg()
  results.options.least_squares_cache_region_indices.clear();
  for(i=0;i<(long long)results.options.least_squares_cache_filenames.size();i++){
    if(((int)results.options.least_squares_cache_filenames.at(i).find("Region"))!=(int)string::npos){
      stream1.clear();
      stream1.str(results.options.least_squares_cache_filenames.at(i).substr(7,6));
      //cout<<"number from region file: "<<results.options.least_squares_cache_filenames.at(i).substr(7,6)<<endl;
      stream1 >> j;
      results.options.least_squares_cache_region_indices.push_back(j-1);
    }
  }

  //Now that ls_cache_region_indices is known, use that with
  //region.indices_to_analyze to figure out how many REQUESTED region
  //files are in this subfolder.
  for(i=0;i<(long long)region.indices_to_analyze.size();i++){
    //Is this requested region one of the ls_cache_region_indices?
    if(count(results.options.least_squares_cache_region_indices.begin(),results.options.least_squares_cache_region_indices.end(),(long long)region.indices_to_analyze.at(i)) == 1)
      results.options.ps.initial_completeness.at(i) = 1;
  }

  results.options.ps.partial_save_code_used=0;//Have to prove ps code was used!

  //Loop through requested region indices and look for partial save files.
  for(i=0;i<(long long)region.indices_to_analyze.size();i++){
    sprintf(s,"partial_save_details_region_%06lld.txt",region.indices_to_analyze.at(i)+1);
    //Save this filename to the partial save filenames vector even if it's
    //not here- that way the filename will be available for use in
    //new regions.
    results.options.ps.filenames.at(i) = s;

    //See if the filenames vector contains this filename.
    for(j=0;j<(long long)results.options.least_squares_cache_filenames.size();j++){
      if(((int)results.options.least_squares_cache_filenames.at(j).find(s))!=(int)string::npos){
        //Note that this subfolder used partial save/load code (yes, repeatedly).
        results.options.ps.partial_save_code_used=1;

        //Check to make sure that a matching cache file was found.
        if(results.options.ps.initial_completeness.at(i) != 1) cout<<"!!!WARNING!!! results.options.ps.initial_completeness["<<i<<"] = "<<results.options.ps.initial_completeness.at(i)<<endl;

        //Note that this particular p/s details file exists, but assume
        //that it's incomplete unless read_ps_details_file() disagrees.
        results.options.ps.initial_completeness.at(i) = 2;
      }
    }//End o loop through results.options.least_squares_cache_filenames.
  }

  //If necessary, read the partial save information now.
  if(results.options.ps.partial_save_code_used==1){
    for(i=0;i<(long long)results.options.ps.filenames.size();i++){
      //The current index of ps.filenames (equivalent to region.i_t_a) is
      //stored in requested_index as it will be in the region loop of
      //gaia.
      results.options.requested_index = i;
      //Read the file if it currently exists.
      if(results.options.ps.initial_completeness.at(i) == 2)
        read_partial_save_details_file(results);
    }
  }

  //Now use initial_completeness to return a single variable
  //"completed_requested_regions" which works exactly like
  //"largest_file_count" used to, but actually takes into account
  //whether or not the cache files in question were requested.
  //(Also calculate partial_requested_regions which is similar.)
  results.options.ps.completed_requested_regions=0;
  results.options.ps.partial_requested_regions=0;
  int check_old_school = 0;//Make sure values of 1 and 3 aren't both here!
  for(i=0;i<(long long)results.options.ps.initial_completeness.size();i++){
    //A value of 1 means this file was created with old
    //routines and doesn't have a partial save details file, so it's
    //presumed to be complete.
    //A value of 3 means that this file did have a partial save details
    //file, but it says the cache file is completed.
    //IMPORTANT: BOTH VALUES CANNOT BE PRESENT IN THE SAME FOLDER!
    if(results.options.ps.initial_completeness.at(i) == 1){
      results.options.ps.completed_requested_regions++;
      if(check_old_school == 0) check_old_school = 1;
      if(check_old_school == 3) cout<<"!!!WARNING!!! Subfolder "<<results.options.least_squares_cachesubfolder<<" appears to contain a mix of cache files with no details files (old) and cache files with details files. This shouldn't happen!"<<endl;
    }
    if(results.options.ps.initial_completeness.at(i) == 2){
      results.options.ps.partial_requested_regions++;
      if(check_old_school == 0) check_old_school = 3;//Partial code used!
      if(check_old_school == 1) cout<<"!!!WARNING!!! Subfolder "<<results.options.least_squares_cachesubfolder<<" appears to contain a mix of cache files with no details files (old) and cache files with details files. This shouldn't happen!"<<endl;
    }
    else if(results.options.ps.initial_completeness.at(i) == 3){
      results.options.ps.completed_requested_regions++;
      if(check_old_school == 0) check_old_school = 3;//Partial code used!
      if(check_old_school == 1) cout<<"!!!WARNING!!! Subfolder "<<results.options.least_squares_cachesubfolder<<" appears to contain a mix of cache files with no details files (old) and cache files with details files. This shouldn't happen!"<<endl;
    }
  }

  //Now that an exact match has been found, look for the files
  //that contain details of "partial saves." Partial saves
  //occur when the notification function is called, and saves
  //the CURRENT A and b matrices to disk, based on the input
  //points that have been looped over by that point.

  //If no partial save detail files are found, the ls cache
  //subfolder is assumed to be "complete", in the sense that
  //all input points have been looped over. The partial save
  //detail files will only be missing if the ls cache subfolder
  //was created before the partial save detail code was written.

  //The earlier code attempts to choose the ls cache
  //subfolder that has the most completed regions, but the
  //"completeness" of partial saves isn't considered. That's
  //because this code only activates on exact parameter matches-
  //it shouldn't be possible to have multiple subfolders that
  //exactly match the requested parameters.

  //It isn't allowed to load parameter subsets with only
  //partially completed regions because the entire point of
  //loading a partial cache subfolder is to MODIFY it, something
  //that shouldn't be done to a folder that contains a superset
  //of the currently requested parameters.
  //NOTE: PARAM SUBSET CODE CAN WORK IF A COMPLETELY NEW FOLDER
  //IS MADE TO MODIFY (WITH SUBSETS OF CURRENT MATRICES COPIED).
  //IF THIS IS DONE, IT'S A GOOD IDEA TO ADD COMPLETENESS TO THE
  //LIST OF CRITERIA FOR SELECTING. OTHERWISE INCOMPLETE PARAM
  //SUPERSETS MIGHT BE CHOSEN INSTEAD OF COMPLETE ONES!
}

void perform_partial_save(least_squares_s &least_squares,
                          input_s &input,
                          results_s &results,
                          long long &i){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  string temp_details_filename,temp_cache_filename;
  FILE *output, *fp;
  string temp_string;
  int cache_file_exists=0,details_file_exists=0;

  //Just checking...
  if(results.options.ps.initial_completeness.at(results.options.requested_index) == 3)
    cout<<"!!!WARNING!!! This region (requested_index = "<<results.options.requested_index<<" and region_index = "<<results.options.region_index<<") was supposedly finished already, but it's being sent to perform_partial_save()!"<<endl;

  //Now, need to update the ps.* vectors with the latest thing
  results.options.ps.input_numpoints.at(results.options.requested_index) = input.time.size();
  results.options.ps.previous_input_index.at(results.options.requested_index) = i-1;//Because THIS i isn't added yet!
  results.options.ps.input_epoch.at(results.options.requested_index) = input.epoch;
  results.options.ps.first_input_time.at(results.options.requested_index) = input.time.front()+input.epoch;
  results.options.ps.last_input_time.at(results.options.requested_index) = input.time.back()+input.epoch;
  results.options.ps.first_used_input_time.at(results.options.requested_index) = input.time.front()+input.epoch;
  results.options.ps.last_used_input_time.at(results.options.requested_index) = input.time.at(i-1)+input.epoch;

  //First, this is the cache filename we're looking for- note
  //that the number in the file is the region_index PLUS ONE.
  temp_cache_filename = create_least_squares_cache_filename(results);

  //Make sure cache file exists by trying to open it in read mode.
  temp_string = results.options.least_squares_cachefolder;
  temp_string.append(results.options.least_squares_cachesubfolder);
  temp_string.append("/");
  temp_string.append(temp_cache_filename);
  fp = fopen(temp_string.c_str(),"r");
  //If file exists (i.e. open succeeded), note that and move file.
  if(fp){
    //Close file and note that it exists.
    fclose(fp);
    cache_file_exists=1;

    //THIS CODE ABANDONED IN FAVOR OF DISTRIBUTED POPULATION/SOLN.
    //Make sure old_ cache file doesn't exist by trying to open it in
    //read mode.
    temp_string = results.options.least_squares_cachefolder;
    temp_string.append(results.options.least_squares_cachesubfolder);
    temp_string.append("/old_");
    temp_string.append(temp_cache_filename);
    fp = fopen(temp_string.c_str(),"r");
    if(fp){
      /*//The old_ file shouldn't be here!
      results.options.stop_analysis=1; NEEDS TO BE INIT'D!
      results.options.stop_analysis_reason="File old_";
      results.options.stop_analysis_reason.append(temp_cache_filename);
      results.options.stop_analysis_reason.append(" shouldn't exist.");

      still have to think about cancelling other regions, initing variable and copying it to all the various copies, seq and parallel, cancelling other reading and writing operations... hmm... parallel could be very tricky but then who cares if the other regions keep trying to run? As long as they're "safe" in the sense that they don't over-write files... what's the problem?

      also, need to copy this code to the details file below.//*/
      return;
    }

    //Move old cache file to a new filename prepended with "old_".
    temp_string = "cd ";
    temp_string.append(results.options.least_squares_cachefolder);
    temp_string.append(results.options.least_squares_cachesubfolder);
    temp_string.append(" ; mv ");
    temp_string.append(temp_cache_filename);
    temp_string.append(" old_");
    temp_string.append(temp_cache_filename);
    //cout<<"cache file mv string: "<<temp_string<<endl;
    output = popen(temp_string.c_str(),"r");
    pclose(output);

    //Note the new absolute temporary cache filename.
    temp_string = results.options.least_squares_cachefolder;
    temp_string.append(results.options.least_squares_cachesubfolder);
    temp_string.append("/old_");
    temp_string.append(temp_cache_filename);
    temp_cache_filename = temp_string;
  }

  //Also, this is the details filename we're looking for, indexed by
  //requested_index because ps.filenames has same length as
  //region.indices_to_analyze.
  temp_details_filename=results.options.ps.filenames.at(results.options.requested_index);

  //Move old details file to a new filename prepended with "old_".
  //1st, make sure file doesn't exist by trying to open it in read mode.
  //Make sure details file exists by trying to open it in read mode.
  temp_string = results.options.least_squares_cachefolder;
  temp_string.append(results.options.least_squares_cachesubfolder);
  temp_string.append("/");
  temp_string.append(temp_details_filename);
  fp = fopen(temp_string.c_str(),"r");
  //If file exists (i.e. open succeeded), note that and move file.
  if(fp){
    fclose(fp);
    details_file_exists=1;

    //Move old details file to a new filename prepended with "old_".
    temp_string = "cd ";
    temp_string.append(results.options.least_squares_cachefolder);
    temp_string.append(results.options.least_squares_cachesubfolder);
    temp_string.append(" ; mv ");
    temp_string.append(temp_details_filename);
    temp_string.append(" old_");
    temp_string.append(temp_details_filename);
    //cout<<"details file mv string: "<<temp_string<<endl;
    output = popen(temp_string.c_str(),"r");
    pclose(output);

    //Note the new absolute temporary details filename.
    temp_string = results.options.least_squares_cachefolder;
    temp_string.append(results.options.least_squares_cachesubfolder);
    temp_string.append("/old_");
    temp_string.append(temp_details_filename);
    temp_details_filename = temp_string;
  }

  //Allow for one or both of the files to not be here, but check that
  //against initial_completeness and warn if these are unexpected absences!

  //For instance, it wouldn't make sense for a cache file to be missing
  //if initial_completeness was 1,2.
  if(cache_file_exists == 0 and (results.options.ps.initial_completeness.at(results.options.requested_index) == 1 or results.options.ps.initial_completeness.at(results.options.requested_index) == 2))
    cout<<"!!!WARNING!!! This region (requested_index = "<<results.options.requested_index<<" and region_index = "<<results.options.region_index<<") was supposed to have a cache file present, but it didn't!"<<endl;

  //Also, it wouldn't make sense for a details file to be missing
  //if initial_completeness was 2.
  if(details_file_exists == 0 and results.options.ps.initial_completeness.at(results.options.requested_index) == 2)
    cout<<"!!!WARNING!!! This region (requested_index = "<<results.options.requested_index<<" and region_index = "<<results.options.region_index<<") was supposed to have a details file present, but it didn't!"<<endl;

  //Then save new cache and details files to disk.
  write_partial_save_details_file(results);
  least_squares_save_to_cache(least_squares,results,input);

  //Then delete old* files, BUT ONLY IF THEY WERE FOUND HERE ORIGINALLY!
  if(details_file_exists == 1)
    if(remove(temp_details_filename.c_str()) != 0) cout<<"Problem deleting temp details file "<<temp_details_filename<<endl;
  if(cache_file_exists == 1)
    if(remove(temp_cache_filename.c_str()) != 0) cout<<"Problem deleting temp cache file "<<temp_cache_filename<<endl;
}

least_squares_s create_least_squares_in_parallel(input_s &input,
                                                 grid_s &grid,
                                                 results_s &results){
  /**********************************************************************
  Purpose: This function creates the H^T*H,H^T*y matrices for least squares.
          It searches the cache, or calls create_least_squares once or
          multiple times (latter using MPI) if cache isn't found.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  least_squares_s least_squares,temp_least_squares;//temp for parallel
  input_s temp_input;//Parallel loops use this struct to populate.
  //Requires GSL libraries.
  #ifdef GSL_HERE

  long long i,j,k,l;

  long long time_total;//Used for reporting total time.
  time_t start_time=-1,end_time;

  //Used to calculate cos/sin(omega*t) at each input time for efficiency.
  //vector<double> sinvalues,cosvalues;
  //Used to move the switch-case statements completely outside of the grid
  //loop.  There are output_type.size() entries, each multiplies da_dmj.
  vector<double> param_coefs;

  //The next variables are used to load data from cache or save to cache.
  FILE *fp;//File pointer
  int found_file;//==1 if specific region's file was found in subfolder.
  long long temp_long;
  double temp_double;
  string foldername, current_foldername,true_foldername,filename,input_file;

  //Subset match variables.
  long long cache_dimension;
  long long desired_row_index,desired_col_index;
  vector<long long>::iterator iparam;//Used to search lspi vector.
  vector<long long>::iterator igrid;//Used to search region.all_grid_indices.
  long long pos;//Contains iparam converted to int index to access lspi.

  //Initialized all 0- same length as desired b vector, then as the desired
  //gsl b vector is written to at index "k", index k in vector_debug +1.
  //If this vector isn't all 1's at end of loading, PRINT OBVIOUS WARNING!
  vector <int> vector_debug;
  //Initialized all 0- same length as desired A matrix, then as the desired
  //gsl A matrix is written to at (row,col) (k,j), increment matrix_debug
  //at outer index k and inner index j by "+1".
  //If this matrix isn't all 1's at end of loading, PRINT OBVIOUS WARNING!
  vector< vector<int> > matrix_debug;

  //This vector has same length as output_type.size() in cached
  //region, each entry is either -1 (don't load this parameter)
  //or it holds the index where that parameter should be loaded.
  vector<long long> parameter_loading_index;

  //This vector has same length as number of grid points in cached
  //region, each entry is either -1 (don't load this grid point)
  //or it holds the index where that grid point should be loaded.
  vector<long long> grid_loading_index;

  //Build vector that has the size of the cached b vector.  Each entry
  //is either -1 (ignore this row/column) or holds the row index in the
  //"desired" b vector in which to save the value from the cached
  //vector at the row index given by its own index.  Despite the name,
  //this vector should apply equally well to the columns.  I'm only
  //copying this vector to cached_cols for readability concerns, and
  //much farther down the list, future expansions.
  vector<long long> cached_rows_loading_index,cached_cols_loading_index;

  long long discarded_grid_points=0;

  DIR *cacheDIR;
  struct dirent *cacheDirEnt;
  string newfile;
  char s[max_length];

  char* f_return;//Used to stop fgets return value warning.

  //Used for converting timespans in seconds to human readable units.
  timespan_conversion_s timespan_conversion;

  //Used for cross product function - to debug the longitude "smearing"
  cross_product_s cross_product;

  //To over-ride already_moved_to_cache, if necessary.
  results.options.loading_completed_matrices=0;

  //If so noted in the l_s variable, load from cache here and return.

  //This code runs only in the case of an exact parameter match when there
  //is a completed cache file for this region.
  if(results.options.least_squares_cache_load == 1 and (results.options.ps.initial_completeness.at(results.options.requested_index) == 1 or results.options.ps.initial_completeness.at(results.options.requested_index) == 3)){

    //To over-ride already_moved_to_cache, if necessary.
    results.options.loading_completed_matrices=1;

    least_squares_load_from_cache(least_squares, results,grid);
    if(results.options.found_file == 1){
      return least_squares;
    }//End of "if cache FILE was found"
    else cout<<"!!WARNING!! File for region "<<results.options.region_index+1<<" was not found in folder "<<results.options.least_squares_cachesubfolder<<", even though results.options.ps.initial_completeness.at("<<results.options.requested_index<<") = "<<results.options.ps.initial_completeness.at(results.options.requested_index)<<endl;
  }//End of "exact complete match found" if statement.
  //This code runs only in the case of an exact parameter match when there
  //is a partially completed cache file for this region.
  else if(results.options.least_squares_cache_load == 1 and results.options.ps.initial_completeness.at(results.options.requested_index) == 2){

    least_squares_load_from_cache(least_squares, results,grid);

    //The b vector should be in units of nanometers, but the loops inside
    //create_least_squares() sums it up in meters for efficiency and
    //then converts to nanometers all at once at the end.
    //Because the files hold values in nanometers, they need to be
    //changed back to the original units (m) that the loops use.
    scale_b_vector(least_squares,results,(double)1E-9);

    if(results.options.found_file != 1)
      cout<<"!!WARNING!! File for region "<<results.options.region_index+1<<" was not found in folder "<<results.options.least_squares_cachesubfolder<<", even though results.options.ps.initial_completeness.at("<<results.options.requested_index<<") = "<<results.options.ps.initial_completeness.at(results.options.requested_index)<<endl;
  }//End of "exact incomplete match found" if statement.
  //Re-ordered or subset match.
  else if(results.options.least_squares_cache_load == 2){
    //Subset matching is never used in the partial save
    //scheme, so this code remains here rather than going into
    //least_squares_load_from_cache().

    //Even if the folder was found, need to search for this region's FILE.
    //This search is kind of redundant because it's been done already, but
    //I'm keeping it here until I know it's safe to delete it (at the very
    //least, it's a good check, right?).
    //But the file had to have been found already b/c otherwise the input
    //would have been loaded in gaia!
    found_file=0;//Init as 0 because it hasn't found region in cache yet.

    //First, figure out exactly what filename we're looking for- note
    //that the number in the file is the region index PLUS ONE.
    filename = create_least_squares_cache_filename(results);

    //Open the cache subfolder.
    foldername = results.options.least_squares_cachefolder;
    foldername.append(results.options.least_squares_cachesubfolder);
    cacheDIR = opendir(foldername.c_str());

    //Search for the file with a name that matches "filename".
    cacheDirEnt = readdir(cacheDIR);
    while(cacheDirEnt != NULL && found_file == 0){
      //If this file matches current region, set found_file=1 to exit.
      if(strcmp(filename.c_str(),cacheDirEnt->d_name)==0) found_file = 1;

      //printf("%s\n",cacheDirEnt->d_name);
      cacheDirEnt = readdir(cacheDIR);
    }

    //Release the open folder.
    closedir(cacheDIR);

    //If this subfolder is a superset of desired options, loading is hard.
    if(found_file == 1){
      cout<<results.options.prefix<<": Loading subsets of matrices from cache."<<endl;

      //To over-ride already_moved_to_cache, if necessary.
      results.options.loading_completed_matrices=1;

      //ls.dimension is calculated and gsl vectors/matrices allocated here.
      allocate_least_squares_matrices(least_squares, grid, results);

      //Prepend directory to filename, then open.
      input_file = foldername;
      input_file.append("/");
      input_file.append(filename);
      fp = fopen(input_file.c_str(),"r");

      //Ignore header line.
      f_return=fgets(s,max_length,fp);

      //Load the first header line which contains cache_dimension.
      f_return=fgets(s,max_length,fp);
      sscanf(s,"%lld", &temp_long);
      cache_dimension = temp_long;//Just a temp variable!

      //Ignore line before b vector.
      f_return=fgets(s,max_length,fp);

      //Initialize vector to debug b vector.
      vector_debug.clear(); vector_debug.resize(least_squares.dimension,0);

      //Before creating parameter_loading_index, I need to create a copy of
      //a_o.l_s_p_i that has two entries for sinusoids.
      //This vector has length output_type.size() in desired matrix. Each entry has
      //the entry from l_s_p_i, but the sines (2nd) position of the
      //sinusoids parameters is -1. This allows the parameter_loading_index
      //to search full_parameter_indices for an entry that matches the
      //current index in ls_cache_parameter_type and know where to load
      //that cached parameter in the actual (output_type.size()) parameter list.
      vector<long long> full_parameter_indices;
      full_parameter_indices.clear();
      full_parameter_indices.resize(results.options.output_type.size(),-1);
      j=0;//Index in full_parameter_indices.
      for(k=0;k<(long long)results.options.least_squares_parameter_indices.size();k++){
        //New parameter types should be added here.
        if(results.options.parameter_type.at(k) < 100){
          full_parameter_indices.at(j) = results.options.least_squares_parameter_indices.at(k);
          j++;
        }
        else if(results.options.parameter_type.at(k) == 101){
          full_parameter_indices.at(j) = results.options.least_squares_parameter_indices.at(k);
          j+=2;//Skip the second sinusoid entry.
        }
        else if(results.options.parameter_type.at(k) >= 10000 and results.options.parameter_type.at(k) < 20000){
          full_parameter_indices.at(j) = results.options.least_squares_parameter_indices.at(k);
          j++;
        }
        else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.y"<<endl;
      }
      if(j != (long long)full_parameter_indices.size()) cout<<"full_parameter_indices's j ended at: "<<j<<" but the full_parameter_indices.size() is: "<<full_parameter_indices.size()<<endl;

      //This vector has same length as output_type.size() in cached
      //region, each entry is either -1 (don't load this parameter)
      //or it holds the index where that parameter should be loaded.
      parameter_loading_index.clear();
      parameter_loading_index.resize(results.options.ls_cache_h_size,-1);
      j=0;//this is the index in p_l_i.
      for(k=0;k<(long long)results.options.ls_cache_parameter_type.size();k++){
        //Is this parameter in the desired matrix?
        pos = -1;
        iparam = find(full_parameter_indices.begin(),full_parameter_indices.end(),k);
        if(iparam != full_parameter_indices.end()) pos = (long long)(iparam - full_parameter_indices.begin());

        if(pos >= 0){
          //New parameter types should be added here.
          if(results.options.ls_cache_parameter_type.at(k) < 100){
            parameter_loading_index.at(j) = pos;
            j++;
          }
          else if(results.options.ls_cache_parameter_type.at(k) == 101){
            parameter_loading_index.at(j) = pos;
            pos++;
            j++;
            parameter_loading_index.at(j) = pos;
            j++;
          }
          else if(results.options.ls_cache_parameter_type.at(k) >= 10000 and results.options.ls_cache_parameter_type.at(k) < 20000){
            parameter_loading_index.at(j) = pos;
            j++;
          }
          else cout<<"!!WARNING!! Parameter type "<<results.options.ls_cache_parameter_type.at(k)<<" was not recognized.u"<<endl;
        }
        else{
          //New parameter types should be added here.
          if(results.options.ls_cache_parameter_type.at(k) < 100) j++;
          else if(results.options.ls_cache_parameter_type.at(k)==101) j+=2;
          else if(results.options.ls_cache_parameter_type.at(k) >= 10000 and results.options.ls_cache_parameter_type.at(k) < 20000) j++;
          else cout<<"!!WARNING!! Parameter type "<<results.options.ls_cache_parameter_type.at(k)<<" was not recognized.i"<<endl;
        }
      }//end of param loop
      if(j != results.options.ls_cache_h_size) cout<<"param_loading_index's j ended at: "<<j<<" but the results.options.ls_cache_h_size is: "<<results.options.ls_cache_h_size<<endl;

      //This vector has same length as number of grid points in cached
      //region, each entry is either -1 (don't load this grid point)
      //or it holds the index where that grid point should be loaded.
      if(results.options.grid_subset == 0){
        //No grid subset means this is very simple.
        grid_loading_index.clear();
        for(l=0;l<(long long)results.options.ls_region.all_grid_indices.at(results.options.region_index).size();l++) grid_loading_index.push_back(l);
      }
      else if(results.options.grid_subset == 1){
        cout<<"Grid subset code activated."<<endl;
        cout<<"!!!!NOTE: This will result in a matrix that contains contributions from input points that are outside of the currently requested region.  Be careful!"<<endl;

        if((long long)results.options.current_region.center_grid_indices.at(results.options.region_index).size() != (long long)results.options.ls_region.center_grid_indices.at(results.options.region_index).size()) cout<<"WARNING!!! Cached center grid.lat.size() "<<(long long)results.options.ls_region.center_grid_indices.at(results.options.region_index).size()<<" doesn't match current center grid.lat.size() "<<(long long)results.options.current_region.center_grid_indices.at(results.options.region_index).size()<<endl;

        //Grid subset code loops through all cached grid points, sees if
        //they are also loaded in desired matrix.  If so, the index where
        //this l'th grid point belongs in the desired matrix is stored
        //in grid_loading_index.  If not, -1 is stored there in g_l_i.
        discarded_grid_points=0;
        grid_loading_index.clear();
        grid_loading_index.resize((long long)results.options.ls_region.all_grid_indices.at(results.options.region_index).size(),-1);
        for(l=0;l<(long long)results.options.ls_region.all_grid_indices.at(results.options.region_index).size();l++){
          //Is this cached grid point also in the desired grid?
          pos = -1;
          //cout<<"results.options.region_index = "<<results.options.region_index<<endl;
          //cout<<"results.options.ls_region.all_grid_indices.at(results.options.region_index="<<results.options.region_index<<").at(l):  "<<results.options.ls_region.all_grid_indices.at((long long)results.options.region_index).at(l)<<endl;
          //if(l<(long long)results.options.current_region.all_grid_indices.at(results.options.region_index).size()) cout<<"results.options.current_region.all_grid_indices.at(results.options.region_index="<<results.options.region_index<<").at(l):  "<<results.options.current_region.all_grid_indices.at((long long)results.options.region_index).at(l)<<endl;
          igrid = find(results.options.current_region.all_grid_indices.at(results.options.region_index).begin(), results.options.current_region.all_grid_indices.at(results.options.region_index).end(), results.options.ls_region.all_grid_indices.at(results.options.region_index).at(l));
          if(igrid != results.options.current_region.all_grid_indices.at(results.options.region_index).end()) pos = (long long)(igrid - results.options.current_region.all_grid_indices.at(results.options.region_index).begin());

          if(pos >= 0) grid_loading_index.at(l) = pos;
          else{
            //cout<<"Grid point "<<l+1<<" in the cached region is being discarded."<<endl;
            discarded_grid_points++;
          }
        }

        if((long long)results.options.ls_region.edge_grid_indices.at(results.options.region_index).size() - (long long)results.options.current_region.edge_grid_indices.at(results.options.region_index).size() != discarded_grid_points or count(grid_loading_index.begin(),grid_loading_index.end(),-1) != discarded_grid_points) cout<<"WARNING!  PROBLEM FOUND WHEN REMOVING GRID POINTS!"<<endl;
      }
      else cout<<"WARNING! results.options.grid_subset = "<<results.options.grid_subset<<endl;

      //Build vector that has the size of the cached b vector.  Each entry
      //is either -1 (ignore this row/column) or holds the row index in the
      //"desired" b vector in which to save the value from the cached
      //vector at the row index given by its own index.  Despite the name,
      //this vector should apply equally well to the columns.  I'm only
      //copying this vector to cached_cols for readability concerns, and
      //MAYBE with concern for future expansions.
      cached_rows_loading_index.clear();
      cached_rows_loading_index.resize(cache_dimension,-1);
      //"j" is the index in cached_rows_loading_index.
      j=0;//Increments each time an element is saved.  Should equal cach_dim
      //Outer loop goes through cached grid points in this region.
      for(l=0;l<(long long)grid_loading_index.size();l++){
        //Only load grid points in cache that are in the desired matrix.
        if(grid_loading_index.at(l) >= 0){
        //Cycle through cached parameters - output_type.size() of them. -1 if not loadin
          for(k=0;k<(long long)results.options.ls_cache_h_size;k++){
            if(parameter_loading_index.at(k) >= 0){
              //This j'th row of the cached vector will be saved in the
              //following row of the desired vector:
              desired_row_index = grid_loading_index.at(l)*results.options.output_type.size() + parameter_loading_index.at(k);
              cached_rows_loading_index.at(j) = desired_row_index;
              //cout<<"Saving row "<<j<<" from the cached vector in (desired-vector) row "<<desired_row_index<<endl;
              j++;
            }
            else j++;
          }//end of param loop
        }//end of load gr pt
        else{
          j+= results.options.ls_cache_h_size;
          //cout<<"Skipping grid point in cache that isn't in desired matrix."<<endl;
        }//end of skip gr pt
      }//end of loop through cache grid pts.

      //Doesn't seem necessary to copy, but will help readability.
      cached_cols_loading_index = cached_rows_loading_index;
      //debug
      if(j != cache_dimension) cout<<"cached_rows_loading_index's j ended at: "<<j<<" but the cache_dimension is: "<<cache_dimension<<endl;

      //Read entries of "b" vector from file, copy to least_squares.b.
      //cout<<"least_squares.dimension: "<<least_squares.dimension<<endl;
      //cout<<"cache_dimension: "<<cache_dimension<<endl;
      for(k=0;k<cache_dimension;k++){
        //Read entry from file.
        f_return=fgets(s,max_length,fp);
        //Save to b vector if this value is in desired b vector.
        desired_row_index = cached_rows_loading_index.at(k);
        if(desired_row_index >= 0){
          sscanf(s,"%lg", &temp_double);
          //cout<<"k: "<<k<<"  desired_row_index: "<<desired_row_index<<" value: "<<temp_double<<endl;
          gsl_vector_set(least_squares.b, desired_row_index, temp_double);
          vector_debug.at(desired_row_index)+=1;
        }
      }//End of loop through rows of b.

      //Check to see that b vector was filled correctly.
      //cout<<"TURN B VECTOR CHECK BACK ON!!!!."<<endl;
      for(k=0;k<least_squares.dimension;k++) if(vector_debug.at(k)!=1) cout<<"WARNING!  vector_debug["<<k<<"]: "<<vector_debug.at(k)<<endl;

      //Ignore line between b vector and A matrix.
      f_return=fgets(s,max_length,fp);

      //Initialize matrix to debug A matrix.
      matrix_debug.clear(); matrix_debug.resize(least_squares.dimension);
      for(k=0;k<least_squares.dimension;k++) matrix_debug.at(k).resize(least_squares.dimension,0);

      //Read entries of "A" matrix from file, copy to least_squares.A
      //Note that only upper triangle of A is recorded- it's symmtrc though!
      for(k=0;k<cache_dimension;k++){
        //Load this row or ignore it if it's not to be loaded.
        desired_row_index = cached_rows_loading_index.at(k);
        if(desired_row_index >= 0){
          for(j=k;j<cache_dimension;j++){
            //Read entry from file.
            f_return=fgets(s,max_length,fp);
            //Save to A matrix if this column is in desired A matrix.
            desired_col_index = cached_cols_loading_index.at(j);
            if(desired_col_index >= 0){
              sscanf(s,"%lg", &temp_double);
              gsl_matrix_set(least_squares.A, desired_row_index, desired_col_index, temp_double);
              matrix_debug.at(desired_row_index).at(desired_col_index)+=1;
            }
          }//End of loop through columns of A.
        }//End of "if this row is to be loaded.
        //Else ignore this row (the right hand side of it, at least...)
        else for(j=k;j<cache_dimension;j++) f_return=fgets(s,max_length,fp);
      }//End of loop through rows of b.

      //Check to see that A matrix was filled correctly.
      //cout<<"TURN A MATRIX CHECK BACK ON!!!!."<<endl;
      for(k=0;k<least_squares.dimension;k++) for(j=k;j<least_squares.dimension;j++) if(matrix_debug.at(k).at(j)!=1) cout<<"WARNING!  matrix_debug["<<k<<"]["<<j<<"]: "<<matrix_debug.at(k).at(j)<<endl;

      mirror_across_diagonal(least_squares,results);
      return least_squares;
    }//End of "if cache FILE was found"
    else cout<<results.options.prefix<<" File not found in ls cache folder, so it is being created."<<endl;

  }//End of "re-ordered or subset match found" else statement.

  //Now that it's clearly necessary, calc zone for population.
  define_zone_of_influence(grid, results);

  //If partial matrices were loaded, let the user know.
  if(results.options.ps.initial_completeness.at(results.options.requested_index) == 2){
    cout<<results.options.prefix<<": Matrix population is resuming at "<<setw(5)<<fixed<<setprecision(2)<<(double)(results.options.ps.previous_input_index.at(results.options.requested_index)+1)*100.0/(double)input.time.size()<<"%."<<endl;
  }
  else{//If nothing's been loaded from cache, allocation is necessary.
    //ls.dimension is calculated and gsl vectors/matrices allocated here.
    //But that's only if this hmc writes directly to the gsl containers-
    //otherwise if results.options.already_moved_to_cache==1
    //it clears and resizes the stl multivectors.
    allocate_least_squares_matrices(least_squares, grid, results);
  }

  //If cache not found, run once or in parallel.
  switch(results.options.time_parallel){
    default: cout<<"!!!!WARNING!!!!!! results.options.time_parallel "<<results.options.time_parallel<<" isn't recognized."<<endl;
    case 0:{//Old.
      create_least_squares(least_squares, input, grid, results);
      break;
    }
    case 1:{//Time parallel.
      //Do I need to initialize anything?

      //Split the given input file up into number of chunks... equal to num
      //of processors?  Possibly consider memory limits so that there's a
      //minimum number of chunks?

      for(i=0;i<(long long)results.options.numprocesses;i++){

        //Do this using MPI but control # of processes in similar manner
        //to gaia.
        create_least_squares(temp_least_squares,temp_input, grid, results);

        //Add A,b from MPI message to ongoing sum.


      }//End of loop through regions.
      break;
    }
  }

  end_time = time(NULL);//Record time at end of program.
  time_total = end_time - start_time;
  timespan_conversion.seconds=time_total;
  //always says something like:  population took 38 years, 29 weeks,  6 days,  6 hours, 13 minutes, 33 seconds
  //cout<<results.options.prefix<<" population took"<<sec2human(timespan_conversion)<<endl;

  //Save this calculation in the cache if desired.  OR- if this code is
  //executed when cache_load == 1,2, run anyway.  That means that the
  //loading code executed, but didn't find a file that matches this region.
  //BUT, don't save if the completed region was already moved to cache.
  if(results.options.least_squares_cache_load != 0 and results.options.already_moved_to_cache==0){
    //The region is finished, and p_p_s() subtracts 1 from i automatically.
    i=input.time.size();
    perform_partial_save(least_squares,input,results,i);

  }//end of if(results.options.least_squares_cache_load != 0) (sav 2 cache)
  else if(results.options.already_moved_to_cache==0)
    cout<<"WARNING!  NOT SAVING COVARIANCE MATRIX TO CACHE!"<<endl;

  #endif
  return least_squares;
}

void least_squares_load_from_cache(least_squares_s &least_squares,
                                   results_s &results,
                                   grid_s &grid){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE
  long long j,k;//Counters.

  //The next variables are used to load data from cache or save to cache.
  FILE *fp;//File pointer
  long long temp_long;
  double temp_double;
  string foldername, current_foldername,true_foldername,filename,input_file;

  //This vector has same length as output_type.size() in cached
  //region, each entry is either -1 (don't load this parameter)
  //or it holds the index where that parameter should be loaded.
  vector<long long> parameter_loading_index;

  DIR *cacheDIR;
  struct dirent *cacheDirEnt;
  string newfile;
  char s[max_length];

  char* f_return;//Used to stop fgets return value warning.

  //Even if the folder was found, need to search for this region's FILE.
  results.options.found_file=0;//Init as 0 because it hasn't found region in cache yet.

  //First, figure out exactly what filename we're looking for- note
  //that the number in the file is the region index PLUS ONE.
  filename = create_least_squares_cache_filename(results);

  //Open the cache subfolder.
  foldername = results.options.least_squares_cachefolder;
  foldername.append(results.options.least_squares_cachesubfolder);
  cacheDIR = opendir(foldername.c_str());

  //Search for the file with a name that matches "filename".
  cacheDirEnt = readdir(cacheDIR);
  while(cacheDirEnt != NULL && results.options.found_file == 0){
    //If this file matches current region, set results.options.found_file=1 to exit.
    if(strcmp(filename.c_str(),cacheDirEnt->d_name)==0) results.options.found_file = 1;

    //printf("%s\n",cacheDirEnt->d_name);
    cacheDirEnt = readdir(cacheDIR);
  }

  //Release the open folder.
  closedir(cacheDIR);

  //If this subfolder has exactly the right parameters, loading is easy
  if(results.options.found_file == 1){
    if(results.options.ps.initial_completeness.at(results.options.requested_index) == 2)
      cout<<results.options.prefix<<": Loading partial matrices from cache."<<endl;
    else cout<<results.options.prefix<<": Loading matrices from cache."<<endl;
    //Prepend directory to filename, then open.
    input_file = foldername;
    input_file.append("/");
    input_file.append(filename);
    fp = fopen(input_file.c_str(),"r");

    //Ignore header line.
    f_return=fgets(s,max_length,fp);

    //Load the first header line which contains least_squares.dimension.
    f_return=fgets(s,max_length,fp);
    sscanf(s,"%lld", &temp_long);

    //least_squares.dimension = temp_long;
    //While temp_long is ls.dim, it's set in allocate_ls_matrices() anyway
    //Also, it makes more sense to check it against grid/param size.
    if(temp_long!=(long long)(grid.lat.size()*results.options.output_type.size()))
      cout<<"!!!WARNING!!!! LEAST SQUARES DIMENSION FROM CACHE FILE DOESN'T MATCH PRODUCT OF GRID AND PARAM SIZES!!!!"<<endl;

    //Ignore line before b vector.
    f_return=fgets(s,max_length,fp);

    //This function will either build STL or GSL containers.
    allocate_least_squares_matrices(least_squares, grid, results);

    //Read entries of "b" vector from file, copy to least_squares.b.
    for(k=0;k<least_squares.dimension;k++){
      //Read entry from file.
      f_return=fgets(s,max_length,fp);
      sscanf(s,"%lg", &temp_double);
      //Save to b vector.
      if(results.options.already_moved_to_cache==0 or
  results.options.loading_completed_matrices==1)
        gsl_vector_set(least_squares.b, k, temp_double);
      else
        least_squares.stl_b.at(k) = temp_double;

    }//End of loop through rows of b.

    //Ignore line between b vector and A matrix.
    f_return=fgets(s,max_length,fp);

    //Read entries of "A" matrix from file, copy to least_squares.A
    for(k=0;k<least_squares.dimension;k++){
      for(j=k;j<least_squares.dimension;j++){
        //Read entry from file.
        f_return=fgets(s,max_length,fp);
        sscanf(s,"%lg", &temp_double);
        //Save to A matrix.
        if(results.options.already_moved_to_cache==0 or
  results.options.loading_completed_matrices==1)
          gsl_matrix_set(least_squares.A, k, j, temp_double);
        else
          least_squares.stl_A.at(k).at(j) = temp_double;

      }//End of loop through columns of A.
    }//End of loop through rows of A.
    fclose(fp);

    mirror_across_diagonal(least_squares,results);

  }//End of "results.options.found_file == 1"
  #endif
}

void least_squares_save_to_cache(least_squares_s &least_squares,
                                 results_s &results,
                                 input_s &input){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE
  long long j,k;//Counters.

  //The next variables are used to load data from cache or save to cache.
  FILE *fp;//File pointer
  string foldername,current_foldername,true_foldername,filename,input_file;

  string newfile;

  //Note that the number in the file is the region index PLUS ONE.
  filename = create_least_squares_cache_filename(results);

  //Prepend directory to filename, then open.
  newfile = results.options.least_squares_cachefolder;
  newfile.append(results.options.least_squares_cachesubfolder);
  newfile.append("/");
  newfile.append(filename);
  fp = fopen(newfile.c_str(),"w");
  fprintf(fp,"least_squares.dimension:\n");
  fprintf(fp,"%6lld\n",least_squares.dimension);
  fprintf(fp,"'b' vector:\n");
  //Saving gsl matrices (0) is done differently than stl multivectors (1).
  if(results.options.already_moved_to_cache==0){
    for(j=0;j<least_squares.dimension;j++){
      fprintf(fp,"%22.15E\n",gsl_vector_get(least_squares.b, j));
    }
    fprintf(fp,"'A' matrix (top triangle, column loop is inside row loop):\n");
    for(k=0;k<least_squares.dimension;k++){
      for(j=k;j<least_squares.dimension;j++){
        fprintf(fp,"%22.15E\n",gsl_matrix_get(least_squares.A, k, j));
      }//End of loop through columns of A.
    }//End of loop through rows of A.
  }
  else{//STL multivectors.
    for(j=0;j<least_squares.dimension;j++){
      fprintf(fp,"%22.15E\n",least_squares.stl_b[j]);
    }
    fprintf(fp,"'A' matrix (top triangle, column loop is inside row loop):\n");
    for(k=0;k<least_squares.dimension;k++){
      for(j=k;j<least_squares.dimension;j++){
        fprintf(fp,"%22.15E\n",least_squares.stl_A[k][j]);
      }//End of loop through columns of A.
    }//End of loop through rows of A.
  }
  fclose(fp);

  //Save input verification points vector to disk if not already there.
  //1st, make sure file doesn't exist by trying to open it in read mode.
  newfile = results.options.least_squares_cachefolder;
  newfile.append(results.options.least_squares_cachesubfolder);
  newfile.append("/input_verification_points.txt");
  fp = fopen(newfile.c_str(),"r");
  //If file doesn't exist (i.e. open failed), create it and populate it.
  if(!fp){
    fp = fopen(newfile.c_str(),"w");
    //First line is input.verification_points.size()
    fprintf(fp,"%lld\n",(long long)input.verification_points.size());
    for(j=0;j<(long long)input.verification_points.size();j++){
      fprintf(fp,"%lld\n",input.verification_points.at(j));
    }
    fclose(fp);
  }//End of "read open failed, so create and populate svp file."
  else fclose(fp);//If read open worked, just close file without changes.
  #endif
}

void mirror_across_diagonal(least_squares_s &least_squares,
                            results_s &results){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE

  long long k,j;

  //Now mirror across the diagonal- I'm looping through cells
  //on top of the diagonal and copying them to the cell opposite the
  //diagonal in the bottom triangle.
  //Wait.. which index is "horizontal"?
  if(results.options.already_moved_to_cache==0 or
  results.options.loading_completed_matrices==1){
    for(k=0;k<least_squares.dimension;k++){
      for(j=k+1;j<least_squares.dimension;j++){
        gsl_matrix_set(least_squares.A, j, k,gsl_matrix_get(least_squares.A, k, j));
      }//End of loop through columns of A.
    }//End of loop through rows of A.
  }
  else{
    for(k=0;k<least_squares.dimension;k++){
      for(j=k+1;j<least_squares.dimension;j++){
        least_squares.stl_A[j][k] = least_squares.stl_A[k][j];
      }//End of loop through columns of A.
    }//End of loop through rows of A.
  }
  #endif
}

void define_zone_of_influence(grid_s &grid, results_s &results){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;
  //These variables are used to display run time.
  time_t start_time,end_time,time_total;
  timespan_conversion_s timespan_conversion;

  start_time = time(NULL);//Record time at start of program.

  switch(results.options.zone_of_influence_choice){
    default: cout<<"!!!!WARNING!!!!!! results.options.zone_of_influence_choice "<<results.options.zone_of_influence_choice<<" isn't recognized."<<endl;
    case 0:{//Old.
      cout<<results.options.prefix<<": Defining simplistic zone of influence..."<<endl;
      results.options.zone_of_influence.clear();
      results.options.zone_of_influence.resize(grid.lat.size());
      for(i=0;i<(long long)grid.lat.size();i++){
        results.options.zone_of_influence.at(i).resize(grid.lat.size());
        for(j=0;j<(long long)grid.lat.size();j++){
          results.options.zone_of_influence.at(i).at(j) = j;
        }
      }

      break;
    }

    case 1:{//Spherically symmetric, radius is zone_radius.
      cout<<results.options.prefix<<": Defining zone of influence with radius "<<results.options.zone_radius<<" km."<<endl;
      results.options.zone_of_influence.clear();
      results.options.zone_of_influence.resize(grid.lat.size());
      for(i=0;i<(long long)grid.lat.size();i++){
        //cout<<"Pt "<<i+1<<" of "<<grid.lat.size()<<" is close to: ";
        for(j=0;j<(long long)grid.lat.size();j++){
          //If distance is less than zone_radius, add to zone of influence.
          if(spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j))*planet_avg_radius <= results.options.zone_radius){
             results.options.zone_of_influence.at(i).push_back(j);
             //cout<<"Pt "<<j<<", dist="<<spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j))*planet_avg_radius<<", ";
            }
        }
        //cout<<endl;
      }

      break;
    }
    case 2:{//All input pts' zones exclude primary mascon (under st pt)
      cout<<results.options.prefix<<": Defining zone of influence with radius "<<results.options.zone_radius<<" km, excluding mascon underneath input point."<<endl;
      results.options.zone_of_influence.clear();
      results.options.zone_of_influence.resize(grid.lat.size());
      for(i=0;i<(long long)grid.lat.size();i++){
        //cout<<"Pt "<<i+1<<" of "<<grid.lat.size()<<" is close to: ";
        for(j=0;j<(long long)grid.lat.size();j++){
          //If distance is less than zone_radius, add to zone of influence.
          //BUT DON'T ADD IF i == j!
          if(i != j and spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j))*planet_avg_radius <= results.options.zone_radius){
             results.options.zone_of_influence.at(i).push_back(j);
             //cout<<"Pt "<<j<<", dist="<<spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j))*planet_avg_radius<<", ";
            }
        }
        //cout<<endl;
      }

      break;
    }
    case 3:{//All input pts' zones exclude primary mascon except NP st pts.
      cout<<results.options.prefix<<": Defining zone of influence with radius "<<results.options.zone_radius<<" km, excluding mascon underneath input point (except for NP)."<<endl;
      results.options.zone_of_influence.clear();
      results.options.zone_of_influence.resize(grid.lat.size());
      for(i=0;i<(long long)grid.lat.size();i++){
        //cout<<"Pt "<<i+1<<" of "<<grid.lat.size()<<" is close to: ";
        for(j=0;j<(long long)grid.lat.size();j++){
          //If distance is less than zone_radius, add to zone of influence.
          //BUT DON'T ADD IF i == j! (unless i==0)
          if((i==0 or i != j) and spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j))*planet_avg_radius <= results.options.zone_radius){
             results.options.zone_of_influence.at(i).push_back(j);
             //cout<<"Pt "<<j<<", dist="<<spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j))*planet_avg_radius<<", ";
            }
        }
        //cout<<endl;
      }

      break;
    }
    case 4:{//Only the NP input pts' zone excludes NP mascon.
      cout<<results.options.prefix<<": Defining zone of influence with radius "<<results.options.zone_radius<<" km, excluding mascon underneath input point (only for NP)."<<endl;
      results.options.zone_of_influence.clear();
      results.options.zone_of_influence.resize(grid.lat.size());
      for(i=0;i<(long long)grid.lat.size();i++){
        //cout<<"Pt "<<i+1<<" of "<<grid.lat.size()<<" is close to: ";
        for(j=0;j<(long long)grid.lat.size();j++){
          //If distance is less than zone_radius, add to zone of influence.
          //BUT DON'T ADD IF i == 0 and j==0 (only np st pts exclude...)
          if((i != 0 and j != 0) and spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j))*planet_avg_radius <= results.options.zone_radius){
             results.options.zone_of_influence.at(i).push_back(j);
             //cout<<"Pt "<<j<<", dist="<<spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j))*planet_avg_radius<<", ";
            }
        }
        //cout<<endl;
      }
      break;
    }
  }//End of switch-case

  //Record avg, max, min points in zone_of_influence.
  results.options.zone_avg_pts = 0.0;
  results.options.zone_max_pts = 0;
  results.options.zone_min_pts = 100000;
  for(i=0;i<(long long)results.options.zone_of_influence.size();i++){
    results.options.zone_avg_pts += results.options.zone_of_influence.at(i).size();
    if((long long)results.options.zone_of_influence.at(i).size() > results.options.zone_max_pts) results.options.zone_max_pts = results.options.zone_of_influence.at(i).size();
    if((long long)results.options.zone_of_influence.at(i).size() < results.options.zone_min_pts) results.options.zone_min_pts = results.options.zone_of_influence.at(i).size();
  }
  results.options.zone_avg_pts /= (double)results.options.zone_of_influence.size();
  cout<<results.options.prefix<<": "<<(long long)results.options.zone_of_influence.size()<<" zones defined, each has an avg, max, min "<<results.options.zone_avg_pts<<", "<<results.options.zone_max_pts<<", "<<results.options.zone_min_pts<<" num of pts."<<endl;
  end_time = time(NULL);//Record time at end of program.
  time_total = end_time - start_time;
  timespan_conversion.seconds=time_total;
  if((long long)time_total > 60) cout<<results.options.prefix<<": Zone of influence definition took"<<sec2human(timespan_conversion)<<endl;
}

void notify(notification_s &notification, long long i,
            least_squares_s &least_squares,
            input_s &input,
            results_s &results){
  /**********************************************************************
  Purpose: This function NEEDS TO BE REPLACED WITH VERSION IN AUTONOTIFS!
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Print an estimated time remaining notice at an interval
  //greater than min_interval (in seconds) and < max_interval.
  //(Many variables should have been initialized before this is called!)
  if(((i-notification.starting_index)+1) % notification.interval==0){
    if(notification.current_time != -1) notification.previous_time = notification.current_time;
    else notification.previous_time = notification.start_time;

    notification.current_time = time(NULL);
    notification.time_loop = notification.current_time - notification.previous_time;
    notification.time_total = notification.current_time - notification.start_time;
    notification.time_remaining = (long long)((double)notification.time_total/((double)(i-notification.starting_index+1)/(double)notification.num_loops) - (double)notification.time_total);
    notification.timespan_conversion.seconds=notification.time_remaining;

    //cout<<"i="<<i<<",i-previous_i="<<i-notification.previous_i<<", time_loop="<<notification.time_loop<<", time_total="<<notification.time_total<<", old notif.interval="<<notification.interval;//DON'T FORGET ABOUT TWO MORE DEBUG COUTS, THE SECOND WITH AN "ENDL" BELOW!

    if(notification.time_total >= notification.first_interval){
      //I added notification.starting_index here because it seemed right.
      cout<<notification.prefix<<" is at "<<setw(5)<<fixed<<setprecision(2)<<(double)(i+1)*100.0/(double)(notification.num_loops+notification.starting_index)<<"%.  Time left:"<<sec2human(notification.timespan_conversion)<<endl;
      //Save the matrix as it is now, as well as details like the current
      //input index, the total number of input times in this region, etc.
      if(notification.partial_saves_enabled == 1){

        //Multiply "b" by 1E9 to convert "y" from meters to nanometers.
        //(This conversion is done after the loop where notify() is...)
        scale_b_vector(least_squares,results,(double)1E9);

        perform_partial_save(least_squares,input,results,i);

        //Change back to the original units (m) that the loops use.
        scale_b_vector(least_squares,results,(double)1E-9);
      }
    }
    else{
      //If time_total isn't big enough, increase notif.interv by factor (or
      //less if time_total is close to notif.1st_interv) each time this
      //function is called.
      double factor=2.0;
      if(notification.time_total>0) if((double)notification.first_interval/(double)notification.time_total < factor) factor = (double)notification.first_interval/(double)notification.time_total;
      //cout<<", factor="<<factor;//DEBUG
      //I added notification.starting_index here because it seemed right.
      notification.interval = (long long)((double)(notification.interval+notification.starting_index-notification.previous_i)*factor) + notification.previous_i;//Need to add prev_i bc it's subtracted below.
    }//End of "if time_total isn't big enough"

    //If necessary, adjust notification interval so not too much or too
    //little time goes by between notifications.
    //Use the number of loops that went by between the last notif
    //and the next last notif and the time that went by between those
    //notifs, and multiply the notif.interv by a factor that aims to
    //make the NEXT time equal to the average of the max and min intervals.
    if(notification.time_total >= notification.first_interval and notification.time_loop>0 and (notification.time_loop > notification.max_interval or notification.time_loop < notification.min_interval)){

      //I added notification.starting_index here because it seemed right.
      notification.interval = (long long)((double)(notification.interval+notification.starting_index-notification.previous_i)/((double)notification.time_loop/((double)(notification.max_interval+notification.min_interval)/2.0)));

      //Now that I've changed the number of loops (notification.interval)
      //between notifications based on how much time the last loop took,
      //I need to account for the fact that the next series of loops until
      //the next notification starts at i.
      //I subtracted notif.starting_index here because it seemed right.
      notification.interval += i-notification.starting_index;
    }//End of "if time_total >= first_interval and interval too long/short"
    //Still need to correct for starting at i.
    //I subtracted notif.starting_index here because it seemed right.
    else notification.interval += i - notification.starting_index - notification.previous_i;

    //Now need to check that the number of loops until the next
    //notification (given by notif.interval minus current i) is less
    //than the chosen maximum number of loops between notifications.
    //I subtracted notif.starting_index here because it seemed right.
    if(notification.interval-i>notification.max_notif_interval)
      notification.interval=notification.max_notif_interval+i-notification.starting_index;

    notification.previous_i=i;
    //cout<<", new notif.interval="<<notification.interval<<endl;//DEBUG
  }
}

void scale_b_vector(least_squares_s &least_squares,
                    results_s &results,
                    double scale_factor){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE

  long long templong;
  if(results.options.already_moved_to_cache==1){
    if(least_squares.dimension!=(long long)least_squares.stl_b.size())
      cout<<"!!!WARNING! Trying to scale b vector, but ls.dim = "<<least_squares.dimension<<" while stl_b.size() = "<<least_squares.stl_b.size()<<endl;

    for(templong=0;templong<least_squares.dimension;templong++)
      least_squares.stl_b[templong] *= scale_factor;
  }
  else{
    gsl_blas_dscal((double)scale_factor, least_squares.b);
  }
  #endif
}

void allocate_least_squares_matrices(
                          least_squares_s &least_squares,
                          grid_s &grid,
                          results_s &results){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE
  long long i;

  //Record the dimensions of A,b here.
  least_squares.dimension = (long long)grid.lat.size()*results.options.output_type.size();

  if(results.options.already_moved_to_cache==0 or
  results.options.loading_completed_matrices==1){
    cout<<results.options.prefix<<": Allocating GSL A,b matrices with dimension "<<least_squares.dimension<<endl;
    //Initialize "A" matrix- correct dimensions and populated with 0.0.
    least_squares.A = gsl_matrix_alloc(least_squares.dimension, least_squares.dimension);
    gsl_matrix_set_zero(least_squares.A);

    //Initialize "b" vector- correct dimension and populated with 0.0.
    least_squares.b = gsl_vector_alloc(least_squares.dimension);
    gsl_vector_set_zero(least_squares.b);
  }
  else{
    cout<<results.options.prefix<<": Allocating STL A,b multivectors with dimension "<<least_squares.dimension<<endl;
    //Clear, resize vector, multivector for b vector, A matrix.
    least_squares.stl_b.clear();
    least_squares.stl_A.clear();
    least_squares.stl_b.resize(least_squares.dimension,0.0);
    least_squares.stl_A.resize(least_squares.dimension);

    for(i=0;i<least_squares.dimension;i++)
      least_squares.stl_A.at(i).resize(least_squares.dimension,0.0);

  }
  #endif
}

void create_least_squares(least_squares_s &least_squares,
                          input_s &input,
                          grid_s &grid,
                          results_s &results){
  /**********************************************************************
  Purpose: This function creates the H^T*H,H^T*y matrices for least squares.
          No need to search cache.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE
  long long i,j,k,l,m,n;
  vector<double> sep_vec;//Unit vector - points from GRACE A to B.
  sep_vec.resize(3,0.0);
  double distanceAB;//between GRACE A and B.
  vector<double> grid2A;//vector that points from grid pt to GRACE A.
  grid2A.resize(3,0.0);
  vector<double> grid2B;//vector that points from grid pt to GRACE B.
  grid2B.resize(3,0.0);
  double distanceA,distanceB;//between grid pt and GRACE A, GRACE B.
  double da_dmj;//deriv of along-track acc w.r.t. mascon j's (current) mass.
  long long column;//Counter which is reset after each row is filled.

  //Used to move the switch-case statements completely outside of the grid
  //loop.  There are output_type.size() entries, each multiplies da_dmj.
  vector<double> param_coefs;

  //Used for cross product function - used to debug the longitude "smearing"
  cross_product_s cross_product;

  //Initialize variables for notification function for time remaining.
  notification_s notification;
  //1st notification occurs after two loops, but it usually won't print
  //anything because not enough total_time will have passed (first_interval).
  //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
  notification.interval=2;
  //Desired min and max intervals in seconds b/w notifications.
  notification.first_interval=10;
  notification.min_interval=1*3600, notification.max_interval=2*3600;
  //notification.min_interval=30, notification.max_interval=90;
  notification.start_time=-1; notification.current_time=-1;
  notification.previous_time=-1;//Needs to be initialized
  notification.prefix = results.options.prefix;

  //If partial save code is being used, set the starting index and note
  //that partial saves are enabled.
  if(results.options.ps.partial_save_code_used == 1){
    //Even if there isn't a details file for this particular region,
    //the previous_input_index vector is prefilled with -1, so this works.
    notification.starting_index = results.options.ps.previous_input_index.at(results.options.requested_index)+1;
    notification.partial_saves_enabled = 1;
  }
  else{//Otherwise it all works same as before (for param subset matches).
    notification.starting_index = 0;
    notification.partial_saves_enabled = 0;
  }

  //Used to size notification intervals.
  notification.previous_i=notification.starting_index;
  notification.num_loops = input.time.size()-notification.starting_index;
  //notification.interval will not be allowed to go above this number
  //so that at LEAST a certain number of messages are seen.
  notification.max_notif_interval=(long long)(notification.num_loops/4);

  //Initialized outside of loop for efficiency.
  param_coefs.clear();
  param_coefs.resize(results.options.output_type.size(),0.0);

  //Compute the upper triangle of the symmetric matrix H^T*H.
  cout<<results.options.prefix<<": Populating H^T*H (=A) matrix and H^T*y (=b) vector using choice "<<results.options.h_matrix_choice<<"..."<<endl;

  //Since this matrix is formed via the H matrix, this switch is used:
  switch(results.options.h_matrix_choice){
    default: cout<<"!!!!WARNING!!!!!! results.options.h_matrix_choice "<<results.options.h_matrix_choice<<" isn't recognized."<<endl;
    ///////////////////////////////////////////////////////////////////////
    //Case 1- remember to copy to perform_upward_continuation,ls_cache_init
    ///////////////////////////////////////////////////////////////////////
    case 1:{//GSL format-pt. masses w/ all params for grid pt 1, then all
            //params (sine, cos, trend, const, etc) for grid pt 2, etc.

      //Used for Kahan vector summation to protect low order bits.
      kahan_s kvector; kvector.init = 1;

      //Used for Kahan matrix summation to protect low order bits.
      kahan_s kmatrix; kmatrix.init = 1;

      //Initialize kvector.x_vector - it holds "h_row" in gsl format.
      kvector.x_vector = gsl_vector_alloc(least_squares.dimension);
      gsl_vector_set_zero(kvector.x_vector);
      kvector.size = least_squares.dimension;

      //Initialize kmatrix.x_matrix - it holds a matrix that represents the
      //current input point's contribution to the A matrix.
      kmatrix.x_matrix = gsl_matrix_alloc(least_squares.dimension,least_squares.dimension);
      gsl_matrix_set_zero(kmatrix.x_matrix);
      kmatrix.size = least_squares.dimension;

      //Loop through input points- this is the outer loop because
      //the sep_vec- vector between the satellites, is calculated
      //only once per input time this way.
      notification.start_time = time(NULL);
      for(i=notification.starting_index;i<(long long)input.time.size();i++){

        notify(notification,i,least_squares,input,results);//Periodically print time remaining.

        //Each loop is a row (const input time)-column resets on each loop
        column=0;

        //Calculate sep_vec for this time.
        sep_vec.front() = input.xB.at(i) - input.xA.at(i);
        sep_vec.at(1) = input.yB.at(i) - input.yA.at(i);
        sep_vec.at(2) = input.zB.at(i) - input.zA.at(i);

        //Normalize sep_vec.
        distanceAB = sqrt(pow(sep_vec.front(),2)+pow(sep_vec.at(1),2)+pow(sep_vec.at(2),2));
        sep_vec.front() = sep_vec.front()/distanceAB;
        sep_vec.at(1) = sep_vec.at(1)/distanceAB;
        sep_vec.at(2) = sep_vec.at(2)/distanceAB;

        //Calculate coefficients that are parameter and time specific
        //but not tied to particular grid points.
        //Ex: constant's coefficient is just "1", trend is the input time
        //multiplied by a conversion factor to make the output in cm/year.
        //The type 101 parameters' coefficients are sin/cos(omega*t).
        //The result is a vector with output_type.size() entries- periods count twice!
        j=0;//Index of output vector - periods count twice.
        for(k=0;k<(long long)results.options.parameter.size();k++){
          //New parameter types should be added here.
          if(results.options.parameter_type.at(k)==101){//Harmonic
            param_coefs.at(j)=cos(results.omegas.at(k)*(input.time.at(i)));
            j++;
            param_coefs.at(j)=sin(results.omegas.at(k)*(input.time.at(i)));
            j++;
          }
          else if(results.options.parameter_type.at(k)==0){//Const
            param_coefs.at(j) = 1.0; j++;
          }
          else if(results.options.parameter_type.at(k)<100){//Polynomial
            //Because pow(±0,y) returns ±∞ and raises the divide-by-zero floating-point exception for y an odd integer.
            if(input.time.at(i) != 0) param_coefs.at(j)=pow((input.time.at(i))*inverse_seconds_in_year,results.options.parameter_type.at(k));//Partial of n'th polynomial term.
            else param_coefs.at(j)=0.0;//Won't ever return ±∞ or raise an exception.
            j++;
          }
          else if(results.options.parameter_type.at(k)>=10000 and results.options.parameter_type.at(k)<20000){//Arbitrary
            param_coefs.at(j) = input.arbitrary.y_values.at(k).at(0).at(i); j++;
          }
          else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.fdneru43u84hv"<<endl;
        }//End of loop through parameters.

        //Loop through grid- this is the next loop because this way
        //the distance from the input point(s) to each grid point is only
        //calculated once for each satellite.

        //2008-06-29 upgrade: zone_of_influence allows grid loop to only
        //bother calcing values if grid pt is "close enough" to input pt.
        //(Though I later discovered the summation technique used here
        //wasn't very efficient with the zone of influence code, which is
        //why I created hmchoice 101.)
        long long current_input_binindex = input.regional_binindices.at(i);
        for(l=0;l<(long long)results.options.zone_of_influence.at(current_input_binindex).size();l++){
          j=results.options.zone_of_influence.at(current_input_binindex).at(l);

          //Calculate vector/distance from grid point to input point A,B.
          //Convert from km to m.
          grid2A.front() = 1000.0*(input.xA.at(i) - grid.x.at(j));
          grid2A.at(1) = 1000.0*(input.yA.at(i) - grid.y.at(j));
          grid2A.at(2) = 1000.0*(input.zA.at(i) - grid.z.at(j));
          grid2B.front() = 1000.0*(input.xB.at(i) - grid.x.at(j));
          grid2B.at(1) = 1000.0*(input.yB.at(i) - grid.y.at(j));
          grid2B.at(2) = 1000.0*(input.zB.at(i) - grid.z.at(j));

          //Also calculate distances.  Already converted from km to m.
          distanceA=sqrt(pow(grid2A.front(),2)+pow(grid2A.at(1),2)+pow(grid2A.at(2),2));
          distanceB=sqrt(pow(grid2B.front(),2)+pow(grid2B.at(1),2)+pow(grid2B.at(2),2));

          //Now deriv of along-track acc w.r.t. mascon j's (current) mass.
          //Notice 1E9 factor that allows input accels to be in units of
          //nanometers/second^2.  Also notice the cm2kg conversion(grid() ).
          da_dmj = Big_G*1E9*grid.cm2kg.at(j)*((grid2A.front()*sep_vec.front()+grid2A.at(1)*sep_vec.at(1)+grid2A.at(2)*sep_vec.at(2))/pow(distanceA,3) - (grid2B.front()*sep_vec.front()+grid2B.at(1)*sep_vec.at(1)+grid2B.at(2)*sep_vec.at(2))/pow(distanceB,3));

          //Now loop through all params, const, trend, sine, cosine..
          for(k=0;k<(long long)results.options.output_type.size();k++){
            gsl_vector_set(kvector.x_vector, column,da_dmj*param_coefs.at(k));
            column++;
          }//End of loop through parameters.
        }//End of loop through grid points in zone of influence.

        //Build kmatrix.x_matrix - it holds the contribution to the final
        //"A" matrix from this particular input point.
        gsl_matrix_set_zero(kmatrix.x_matrix);
        for(k=0;k<least_squares.dimension;k++){
          for(j=k;j<least_squares.dimension;j++){
            gsl_matrix_set(kmatrix.x_matrix, k, j,gsl_vector_get(kvector.x_vector, k)*gsl_vector_get(kvector.x_vector, j));
          }//End of loop through columns of A.
        }//End of loop through rows of A.*/

        //Now that this input point's contribution to the H matrix has been
        //calculated as kvector.x_vector, add this row's contribution
        //to the H^T*H(=A) gsl matrix using ksum_matrix().  The sum that
        //will eventually be copied to least_squares.A is currently in
        //kmatrix.sum_matrix.
        ksum_matrix(kmatrix);

        //For the b vector, must multiply kvector.x_vector by
        //input.accel.at(i).  This would screw up the A matrix, which
        //is why the A matrix is done first.
        gsl_vector_scale(kvector.x_vector,input.accel.at(i));

        //Now that this input point's contribution to the H matrix has been
        //calculated as kvector.x_vector, add this row's contribution
        //to the H*y(=b) gsl vector using ksum_vector().  The sum which will
        //eventually be copied to least_squares.b is currently in
        //kvector.sum_vector.
        ksum_vector(kvector);

      }//End of loop through input.

      //Copy the temp location of the b vector to least_squares.b.
      gsl_vector_memcpy(least_squares.b,kvector.sum_vector);

      //Copy the temp location of the A matrix to least_squares.A.
      gsl_matrix_memcpy(least_squares.A,kmatrix.sum_matrix);

      //Now free memory taken up by Kahan temp vectors/matrices.
      gsl_vector_free(kvector.c_vector);
      gsl_vector_free(kvector.x_vector);
      gsl_vector_free(kvector.sum_vector);
      gsl_vector_free(kvector.temp_vector1);
      gsl_matrix_free(kmatrix.c_matrix);
      gsl_matrix_free(kmatrix.x_matrix);
      gsl_matrix_free(kmatrix.sum_matrix);
      gsl_matrix_free(kmatrix.temp_matrix1);

      //Multiply "b" by 1E9 to convert "y" from meters to nanometers.
      gsl_blas_dscal((double)1E9, least_squares.b);

      mirror_across_diagonal(least_squares,results);

      break;
    }
    ///////////////////////////////////////////////////////////////////////
    //Case 2- remember to copy to perform_upward_continuation,ls_cache_init
    ///////////////////////////////////////////////////////////////////////
    case 2:{//GSL format-disk mascons w/ all params for grid pt 1, then all
            //params (sine, cos, trend, const, etc) for grid pt 2, etc.

      //Used for Kahan vector summation to protect low order bits.
      kahan_s kvector; kvector.init = 1;

      //Used for Kahan matrix summation to protect low order bits.
      kahan_s kmatrix; kmatrix.init = 1;

      //Initialize kvector.x_vector - it holds "h_row" in gsl format.
      kvector.x_vector = gsl_vector_alloc(least_squares.dimension);
      gsl_vector_set_zero(kvector.x_vector);
      kvector.size = least_squares.dimension;

      //Initialize kmatrix.x_matrix - it holds a matrix that represents the
      //current input point's contribution to the A matrix.
      kmatrix.x_matrix = gsl_matrix_alloc(least_squares.dimension,least_squares.dimension);
      gsl_matrix_set_zero(kmatrix.x_matrix);
      kmatrix.size = least_squares.dimension;

      //Calculate gamma_coefs (Farrell p773) up to nmax.

      //Should I drop idea of different radii for all gr pts?
      //That would make this calc very time and space intensive...
      //But maybe I could make a vector of longs called mascon_index
      //that reroutes mascon i's details to mascon_index[i].  This vector
      //could have size=i and be filled with zeros for the simplest case
      //where each mascon has same radii and thus cm2kg values.  But later
      //I'll want to change this so that np/sp can have different values
      //from other grid points.  Or maybe grid points could be spaced
      //differently at different lats or be more dense in cont. shelves,
      //or other improvements.  This would help even when the grid
      //becomes more complicated, because I won't have to store separate
      //values of alpha or cos(alpha) for all grid pnts that have the same
      //value of alpha!
      //Might be a good idea to keep note of which vectors use this
      //convention, because if they don't the mascon_index[i] will always
      //be less than or equal to [i].  So referencing a vector with
      //mascon_index[i] rather than 'i' won't produce an immediate error!
      //FOR THE TIME BEING, I'M GOING TO PRETEND THAT THE RADIUS FOR GRID
      //POINT ZERO IS THE SAME AS THE RADII FOR ALL OTHER GRID POINTS!

      //IF I DECIDE TO USE DISK MASCONS EVENTUALLY, I'LL WANT TO USE
      //MASCON_INDEX TO CORRECT THIS SIMPLIFIED APPROACH.  I ALSO WANT TO
      //CORRECT THE AREA CALCULATION SO THAT IT USES THE CIRCULAR AREA OF
      //THE DISK MASCON!!! (NO!)  ALSO, SHOULD I CHANGE RADIUS CALC SO IT
      //USES THE SMALLEST OF LAT OR LON SPACING?  WHAT ABOUT AUTOMATING
      //THE SELECTION OF NMAX BASED ON 20,000KM / 200KM SPACING = 100?
      //FURTHERMORE, WHAT ABOUT ALLOWING NMAX TO VARY FROM MASCON TO MASCON?


      //These vectors have been defined in the grid structure but not
      //used:
      //vector<double> radius;//Radius(km) of each gr pt for disk mascons
      //vector<double> radius_angle;//Radius(radians) for disk mascons
      //remember about area and cm2kg - which are similar but used in
      //a unit conversion sense only in the code.
      double radius, radius_angle, radius_cos;
      //radius = grid.options.lat_spacing;
      radius = results.options.disk_radius;
      radius_angle = atan2(radius,planet_polar_radius);
      radius_cos = cos(radius_angle);
      cout<<"radius, radius_angle, radius_cos: "<<radius<<", "<<radius_angle<<", "<<radius_cos<<endl;
      cout<<"nmax: "<<results.options.nmax<<endl;

      //Precalc Legendre polynomials of cos(alpha) as well as denominator.
      //Legendre polynomials need to be calced to nmax+1
      vector<double> legendre_cos_alpha;
      legendre_cos_alpha.resize(results.options.nmax+1);
      for(i=0;i<=results.options.nmax;i++) legendre_cos_alpha.at(i) = gsl_sf_legendre_Pl(i, radius_cos);

      vector<double> gamma_coefs;
      double gamma_denom;
      //1E6 converts from km^2 to m^2.
      gamma_denom = 4.0*pi*1E6*pow(planet_avg_radius,2)*(1-radius_cos);
      gamma_coefs.resize(results.options.nmax);
      //1E6 converts from km^2 to m^2.
      gamma_coefs.front() = 1/(4.0*pi*1E6*pow(planet_avg_radius,2));
      for(i=1;i<results.options.nmax;i++) gamma_coefs.at(i) = (legendre_cos_alpha.at(i-1)-legendre_cos_alpha.at(i+1))/gamma_denom;

      //Find spherical components of sep_vec at GRACE A and B.
      vector <double> spherical_sepvectorA, spherical_sepvectorB;
      //3 components: radius, theta (polar), phi (azimuthal).
      spherical_sepvectorA.resize(3,0); spherical_sepvectorB.resize(3,0);

      //Quick hack for even more speed!
      double lots_o_constants_and_cm2kg0 = 4*pi*Big_G*1E9*grid.cm2kg.front();

      //Compute the upper triangle of the symmetric matrix H^T*H.
      //Loop through input points- this is the outer loop because
      //the sep_vec- vector between the satellites, is calculated
      //only once per input time this way.
      notification.start_time = time(NULL);
      for(i=notification.starting_index;i<(long long)input.time.size();i++){

        notify(notification,i,least_squares,input,results);//Periodically print time remaining.

        //Each loop is a row (const input time)-column resets on each loop
        column=0;

        //Calculate sep_vec for this time.
        sep_vec.front() = input.xB.at(i) - input.xA.at(i);
        sep_vec.at(1) = input.yB.at(i) - input.yA.at(i);
        sep_vec.at(2) = input.zB.at(i) - input.zA.at(i);

        //Normalize sep_vec.
        distanceAB = sqrt(pow(sep_vec.front(),2)+pow(sep_vec.at(1),2)+pow(sep_vec.at(2),2));
        sep_vec.front() = sep_vec.front()/distanceAB;
        sep_vec.at(1) = sep_vec.at(1)/distanceAB;
        sep_vec.at(2) = sep_vec.at(2)/distanceAB;

        //After normalizing, convert distanceAB from km to m.
        //NOT NECESSARY, BECAUSE DISTANCEAB IS NEVER USED AGAIN!
        //distanceAB = distanceAB*1000.0;

        double radiusA = sqrt(pow(input.xA.at(i),2)+pow(input.yA.at(i),2)+pow(input.zA.at(i),2));
        double radiusB = sqrt(pow(input.xB.at(i),2)+pow(input.yB.at(i),2)+pow(input.zB.at(i),2));

        double phiA = atan2(input.yA.at(i),input.xA.at(i));
        double phiB = atan2(input.yB.at(i),input.xB.at(i));

        double thetaA = atan2(sqrt(pow(input.yA.at(i),2)+pow(input.xA.at(i),2)),input.zA.at(i));
        double thetaB = atan2(sqrt(pow(input.yB.at(i),2)+pow(input.xB.at(i),2)),input.zB.at(i));

        //For Legendre functions.
        double cos_thetaA = cos(thetaA);
        double cos_thetaB = cos(thetaB);

        //For theta component of potential gradient.
        double sin_thetaA = sin(thetaA);
        double sin_thetaB = sin(thetaB);

        //Precalc Legendre polynomials of cos(thetaA,B).
        //Legendre polynomials need to be calced to nmax+1
        vector<double> legendre_cos_thetaA,legendre_cos_thetaB;
        legendre_cos_thetaA.resize(results.options.nmax+1);
        legendre_cos_thetaB.resize(results.options.nmax+1);
        for(k=0;k<=results.options.nmax;k++){
          legendre_cos_thetaA.at(k) = gsl_sf_legendre_Pl(k, cos_thetaA);
          legendre_cos_thetaB.at(k) = gsl_sf_legendre_Pl(k, cos_thetaB);
        }

        //Radial components.
        spherical_sepvectorA.front() = sep_vec.front()*sin_thetaA*cos(phiA)+sep_vec.at(1)*sin_thetaA*sin(phiA)+sep_vec.at(2)*cos_thetaA;
        spherical_sepvectorB.front() = sep_vec.front()*sin_thetaB*cos(phiB)+sep_vec.at(1)*sin_thetaB*sin(phiB)+sep_vec.at(2)*cos_thetaB;

        //Theta (polar) components.
        spherical_sepvectorA.at(1) = sep_vec.front()*cos_thetaA*cos(phiA)+sep_vec.at(1)*cos_thetaA*sin(phiA)-sep_vec.at(2)*sin_thetaA;
        spherical_sepvectorB.at(1) = sep_vec.front()*cos_thetaB*cos(phiB)+sep_vec.at(1)*cos_thetaB*sin(phiB)-sep_vec.at(2)*sin_thetaB;

        //Phi (azimuthal) components.  DON'T NEED UNTIL HAVE >1 MASCON!!!!
        //spherical_sepvectorA.at(1) = 1.0;

        //Calculate coefficients that are parameter and time specific
        //but not tied to particular grid points.
        //Ex: constant's coefficient is just "1", trend is the input time
        //multiplied by a conversion factor to make the output in cm/year.
        //The type 101 parameters' coefficients are sin/cos(omega*t).
        //The result is a vector with output_type.size() entries- periods count twice!
        j=0;//Index of output vector - periods count twice.
        for(k=0;k<(long long)results.options.parameter.size();k++){
          //New parameter types should be added here.
          if(results.options.parameter_type.at(k)==101){//Harmonic
            param_coefs.at(j)=cos(results.omegas.at(k)*(input.time.at(i)));
            j++;
            param_coefs.at(j)=sin(results.omegas.at(k)*(input.time.at(i)));
            j++;
          }
          else if(results.options.parameter_type.at(k)==0){//Const
            param_coefs.at(j) = 1.0; j++;
          }
          else if(results.options.parameter_type.at(k)<100){//Polynomial
            //Because pow(±0,y) returns ±∞ and raises the divide-by-zero floating-point exception for y an odd integer.
            if(input.time.at(i) != 0) param_coefs.at(j)=pow((input.time.at(i))*inverse_seconds_in_year,results.options.parameter_type.at(k));//Partial of n'th polynomial term.
            else param_coefs.at(j)=0.0;//Won't ever return ±∞ or raise an exception.
            j++;
          }
          else if(results.options.parameter_type.at(k)>=10000 and results.options.parameter_type.at(k)<20000){//Arbitrary
            param_coefs.at(j) = input.arbitrary.y_values.at(k).at(0).at(i); j++;
          }
          else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.fdjeru843yfvn"<<endl;
        }//End of loop through parameters.

        //Loop through grid- this is the next loop because this way
        //the distance from the input point(s) to each grid point is only
        //calculated once for each satellite.
        for(j=0;j<(long long)grid.lat.size();j++){

          //Now deriv of along-track acc w.r.t. mascon j's (current) mass.
          //Loop through all n from 0 to nmax to get all degrees of Legendre
          da_dmj = 0.0;

          //Need to do k=0 first because polar components have derivatives
          //of legendre functions that are zero when k=0 but the general
          //formula refers to "k-1" so the formula can't be applied to k=0.
          k=0;

          //This is calculated nmax times, so be efficient about it.
          double orig_radial_factorA = planet_polar_radius/radiusA;
          double orig_radial_factorB = planet_polar_radius/radiusB;
          double radial_factorA = pow(orig_radial_factorA,2);
          double radial_factorB = pow(orig_radial_factorB,2);

          //Radial parts of potential gradient at A,B dotted w/ sep vector
          da_dmj += spherical_sepvectorB.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorB*legendre_cos_thetaB.at(k));
          da_dmj -= spherical_sepvectorA.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorA*legendre_cos_thetaA.at(k));

          //Update radial factors for next (k=1) degree of Legendre expansn.
          radial_factorA *= orig_radial_factorA;
          radial_factorB *= orig_radial_factorB;

          for(k=1;k<results.options.nmax;k++){
            //Radial parts of potential gradient at A,B dotted w/ sep vector
            da_dmj += spherical_sepvectorB.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorB*legendre_cos_thetaB.at(k));
            da_dmj -= spherical_sepvectorA.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorA*legendre_cos_thetaA.at(k));
            //Polar components (azimuthal not needed yet).
            da_dmj += spherical_sepvectorB.at(1)*(gamma_coefs.at(k)/sin_thetaB*(double)(k)/(double)(2*k+1)*radial_factorB*(cos_thetaB*legendre_cos_thetaB.at(k) - legendre_cos_thetaB.at(k-1)));
            da_dmj -= spherical_sepvectorA.at(1)*(gamma_coefs.at(k)/sin_thetaA*(double)(k)/(double)(2*k+1)*radial_factorA*(cos_thetaA*legendre_cos_thetaA.at(k) - legendre_cos_thetaA.at(k-1)));

            //Update radial factors for next loop.
            radial_factorA *= orig_radial_factorA;
            radial_factorB *= orig_radial_factorB;
          }
          //I've factored out some consts as well as old business:
          //Notice 1E9 factor that allows input accels to be in units of
          //nanometers/second^2.  Also notice the cm2kg conversion(grid() ).
          //db lots_o_constants_and_cm2kg0 = 4*pi*Big_G*1E9*grid.cm2kg.front()
          da_dmj *= lots_o_constants_and_cm2kg0;

          //Now loop through all params, const, trend, sine, cosine..
          for(k=0;k<(long long)results.options.output_type.size();k++){
            gsl_vector_set(kvector.x_vector, column,da_dmj*param_coefs.at(k));
            column++;
          }//End of loop through parameters.
        }//End of loop through grid points

        //Build kmatrix.x_matrix - it holds the contribution to the final
        //"A" matrix from this particular input point.
        gsl_matrix_set_zero(kmatrix.x_matrix);
        for(k=0;k<least_squares.dimension;k++){
          for(j=k;j<least_squares.dimension;j++){
            gsl_matrix_set(kmatrix.x_matrix, k, j,gsl_vector_get(kvector.x_vector, k)*gsl_vector_get(kvector.x_vector, j));
          }//End of loop through columns of A.
        }//End of loop through rows of A.

        //Now that this input point's contribution to the H matrix has been
        //calculated as kvector.x_vector, add this row's contribution
        //to the H^T*H(=A) gsl matrix using ksum_matrix().  The sum that
        //will eventually be copied to least_squares.A is currently in
        //kmatrix.sum_matrix.
        ksum_matrix(kmatrix);

        //For the b vector, must multiply kvector.x_vector by
        //input.accel.at(i).  This would screw up the A matrix, which
        //is why the A matrix is done first.
        gsl_vector_scale(kvector.x_vector,input.accel.at(i));

        //Now that this input point's contribution to the H matrix has been
        //calculated as kvector.x_vector, add this row's contribution
        //to the H*y(=b) gsl vector using ksum_vector().  The sum which will
        //eventually be copied to least_squares.b is currently in
        //kvector.sum_vector.
        ksum_vector(kvector);

      }//End of loop through input.

      //Copy the temp location of the b vector to least_squares.b.
      gsl_vector_memcpy(least_squares.b,kvector.sum_vector);

      //Copy the temp location of the A matrix to least_squares.A.
      gsl_matrix_memcpy(least_squares.A,kmatrix.sum_matrix);

      //Now free memory taken up by Kahan temp vectors/matrices.
      gsl_vector_free(kvector.c_vector);
      gsl_vector_free(kvector.x_vector);
      gsl_vector_free(kvector.sum_vector);
      gsl_vector_free(kvector.temp_vector1);
      gsl_matrix_free(kmatrix.c_matrix);
      gsl_matrix_free(kmatrix.x_matrix);
      gsl_matrix_free(kmatrix.sum_matrix);
      gsl_matrix_free(kmatrix.temp_matrix1);

      //Multiply "b" by 1E9 to convert "y" from meters to nanometers.
      gsl_blas_dscal((double)1E9, least_squares.b);

      mirror_across_diagonal(least_squares,results);

      break;
    }
    ///////////////////////////////////////////////////////////////////////
    //Case 101- remember to cp to perform_upward_continuation,ls_cache_init
    ///////////////////////////////////////////////////////////////////////
    case 101:{//Similar to case 1, but much faster because it uses zone
              //of influence to only compute terms in H if they're big.
              //GSL format-pt. masses w/ all params for grid pt 1, then all
              //params (sine, cos, trend, const, etc) for grid pt 2, etc.

      //Used for Kahan vector summation to protect low order bits.
      kahan_s kvector; kvector.init = 1;

      //Used for Kahan matrix summation to protect low order bits.
      kahan_s kmatrix; kmatrix.init = 1;

      //Initialize kvector.x_vector - it holds "h_row" in gsl format.
      kvector.x_vector = gsl_vector_alloc(least_squares.dimension);
      gsl_vector_set_zero(kvector.x_vector);
      kvector.size = least_squares.dimension;

      //Initialize kmatrix.x_matrix - it holds a matrix that represents the
      //current input point's contribution to the A matrix.
      kmatrix.x_matrix = gsl_matrix_alloc(least_squares.dimension,least_squares.dimension);
      gsl_matrix_set_zero(kmatrix.x_matrix);
      kmatrix.size = least_squares.dimension;

      //Loop through input points- this is the outer loop because
      //the sep_vec- vector between the satellites, is calculated
      //only once per input time this way.
      notification.start_time = time(NULL);
      for(i=notification.starting_index;i<(long long)input.time.size();i++){

        notify(notification,i,least_squares,input,results);//Periodically print time remaining.

        //Each loop is a row (const input time)-column resets on each loop
        //Actually, zone of influence code makes this statement unnecessary
        //column=0;

        //Calculate sep_vec for this time.
        sep_vec.front() = input.xB.at(i) - input.xA.at(i);
        sep_vec.at(1) = input.yB.at(i) - input.yA.at(i);
        sep_vec.at(2) = input.zB.at(i) - input.zA.at(i);

        //Normalize sep_vec.
        distanceAB = sqrt(pow(sep_vec.front(),2)+pow(sep_vec.at(1),2)+pow(sep_vec.at(2),2));
        sep_vec.front() = sep_vec.front()/distanceAB;
        sep_vec.at(1) = sep_vec.at(1)/distanceAB;
        sep_vec.at(2) = sep_vec.at(2)/distanceAB;

        //Calculate coefficients that are parameter and time specific
        //but not tied to particular grid points.
        //Ex: constant's coefficient is just "1", trend is the input time
        //multiplied by a conversion factor to make the output in cm/year.
        //The type 101 parameters' coefficients are sin/cos(omega*t).
        //The result is a vector with output_type.size() entries- periods count twice!
        j=0;//Index of output vector - periods count twice.
        for(k=0;k<(long long)results.options.parameter.size();k++){
          //New parameter types should be added here.
          if(results.options.parameter_type.at(k)==101){//Harmonic
            param_coefs.at(j)=cos(results.omegas.at(k)*(input.time.at(i)));
            j++;
            param_coefs.at(j)=sin(results.omegas.at(k)*(input.time.at(i)));
            j++;
          }
          else if(results.options.parameter_type.at(k)==0){//Const
            param_coefs.at(j) = 1.0; j++;
          }
          else if(results.options.parameter_type.at(k)<100){//Polynomial
            //Because pow(±0,y) returns ±∞ and raises the divide-by-zero floating-point exception for y an odd integer.
            if(input.time.at(i) != 0) param_coefs.at(j)=pow((input.time.at(i))*inverse_seconds_in_year,results.options.parameter_type.at(k));//Partial of n'th polynomial term.
            else param_coefs.at(j)=0.0;//Won't ever return ±∞ or raise an exception.
            j++;
          }
          else if(results.options.parameter_type.at(k)>=10000 and results.options.parameter_type.at(k)<20000){//Arbitrary
            param_coefs.at(j) = input.arbitrary.y_values.at(k).at(0).at(i); j++;
          }
          else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.fhre723jfghy"<<endl;
        }//End of loop through parameters.

        //Loop through grid- this is the next loop because this way
        //the distance from the input point(s) to each grid point is only
        //calculated once for each satellite.

        //2008-06-29 upgrade: zone_of_influence allows grid loop to only
        //bother calcing values if grid pt is "close enough" to input pt.
        long long current_input_binindex = input.regional_binindices.at(i);
        for(l=0;l<(long long)results.options.zone_of_influence.at(current_input_binindex).size();l++){
          j=results.options.zone_of_influence.at(current_input_binindex).at(l);
          //When using zone of influence, column needs to be set
          //to the right value for each new grid point in the zone so it
          //properly skips all the parameters for grid points not in zone.
          column = j*results.options.output_type.size();

          //Calculate vector/distance from grid point to input point A,B.
          //Convert from km to m.
          grid2A.front() = 1000.0*(input.xA.at(i) - grid.x.at(j));
          grid2A.at(1) = 1000.0*(input.yA.at(i) - grid.y.at(j));
          grid2A.at(2) = 1000.0*(input.zA.at(i) - grid.z.at(j));
          grid2B.front() = 1000.0*(input.xB.at(i) - grid.x.at(j));
          grid2B.at(1) = 1000.0*(input.yB.at(i) - grid.y.at(j));
          grid2B.at(2) = 1000.0*(input.zB.at(i) - grid.z.at(j));

          //Also calculate distances.  Already converted from km to m.
          distanceA = sqrt(pow(grid2A.front(),2)+pow(grid2A.at(1),2)+pow(grid2A.at(2),2));
          distanceB = sqrt(pow(grid2B.front(),2)+pow(grid2B.at(1),2)+pow(grid2B.at(2),2));

          //Now deriv of along-track acc w.r.t. mascon j's (current) mass.
          //Notice 1E9 factor that allows input accels to be in units of
          //nanometers/second^2.  Also notice the cm2kg conversion(grid()).
          da_dmj = Big_G*1E9*grid.cm2kg.at(j)*((grid2A.front()*sep_vec.front()+grid2A.at(1)*sep_vec.at(1)+grid2A.at(2)*sep_vec.at(2))/pow(distanceA,3) - (grid2B.front()*sep_vec.front()+grid2B.at(1)*sep_vec.at(1)+grid2B.at(2)*sep_vec.at(2))/pow(distanceB,3));

          //Now loop through all params, const, trend, sine, cosine
          //to obtain this gr pt's part of the current row of the H matrix
          //(stored in kvector.x_vector) and the contribution to the b
          //matrix from this grid point's part of this row.
          for(k=0;k<(long long)results.options.output_type.size();k++){
            //Calc the val of H in the i'th row and column'th column.
            //where column = results.options.output_type.size()*j
            //where j = current gr pt in the zone of influence.
            gsl_vector_set(kvector.x_vector, column,da_dmj*param_coefs.at(k));
            //For the b vector, must multiply kvector.x_vector by
            //input.accel.at(i).
            gsl_vector_set(least_squares.b, column, gsl_vector_get(least_squares.b,column) + gsl_vector_get(kvector.x_vector, column)*input.accel.at(i));
            column++;//Another parameter for this grid point.
          }//End of loop through parameters.

          //Now that all parameters for this grid point have been added to
          //the x_vector, the contribution for the A matrix from this grid
          //point's parameters can be calculated.

          //Loop through all the grid points in the current zone of
          //influence for this input point that have already been
          //calculated, including the current one to calculate correlations
          //between different parameters of differen grid points (entries
          //in the A matrix, in other words.)
          //Don't loop through points that haven't already been calced
          //because the contributions from those points will be added when
          //they are calculated later in the big loop thru zone of influ.
          for(long long tik=0;tik <= l;tik++){
            //1st, reset column so it starts over at beginng of param list
            //for current gr pt.
            column = j*results.options.output_type.size();

            //Obtain grid index for this previous point which is also in
            //the zone of influence
            long long new_j=results.options.zone_of_influence.at(current_input_binindex).at(tik);

            //Now loop through parameters of current grid point, where
            //position in the x_vector is kept in "column"
            for(k=0;k<(long long)results.options.output_type.size();k++){
              //This is the index in x_vector where the previous grid pt's
              //parameters begin.
              long long new_column = new_j*results.options.output_type.size();

              //Now loop through parameters of previous grid point, where
              //position in the x_vector is kept in "new_column"
              for(m=0;m<(long long)results.options.output_type.size();m++){

                //if(i==0) cout<<"Incrementing value of A matrix at 1st index new_column="<<new_column<<" and 2nd index "<<column<<" with new value "<<gsl_vector_get(kvector.x_vector, new_column)*gsl_vector_get(kvector.x_vector, column)<<endl;
                gsl_matrix_set(least_squares.A, new_column, column,gsl_matrix_get(least_squares.A,new_column,column) + gsl_vector_get(kvector.x_vector, new_column)*gsl_vector_get(kvector.x_vector, column));
                new_column++;//Increment the OTHER gr pt's parameter.
              }//End of loop through previous grid point parameters.
              column++;//Increment the current gr pt's parameter.
            }//End of loop through current grid point parameters.
          }//End of loop through gr pts in zone that are already computed.
        }//End of loop through grid points in zone of influence.
      }//End of loop through input.

      //Now free memory taken up by Kahan temp vector.
      gsl_vector_free(kvector.x_vector);

      //Multiply "b" by 1E9 to convert "y" from meters to nanometers.
      gsl_blas_dscal((double)1E9, least_squares.b);

      mirror_across_diagonal(least_squares,results);

      break;
    }
    ///////////////////////////////////////////////////////////////////////
    //Case 201- remember to cp to perform_upward_continuation,ls_cache_init
    ///////////////////////////////////////////////////////////////////////
    case 201:{//Uses zone of influence AND support grid.

      //Used for Kahan vector summation to protect low order bits.
      kahan_s kvector; kvector.init = 1;

      //Used for Kahan matrix summation to protect low order bits.
      kahan_s kmatrix; kmatrix.init = 1;

      //Initialize kvector.x_vector - it holds "h_row" in gsl format.
      kvector.x_vector = gsl_vector_alloc(least_squares.dimension);
      gsl_vector_set_zero(kvector.x_vector);
      kvector.size = least_squares.dimension;

      //Initialize kmatrix.x_matrix - it holds a matrix that represents the
      //current input point's contribution to the A matrix.
      kmatrix.x_matrix = gsl_matrix_alloc(least_squares.dimension,least_squares.dimension);
      gsl_matrix_set_zero(kmatrix.x_matrix);
      kmatrix.size = least_squares.dimension;

      cout<<results.options.prefix<<": Inverting using supporting grid of type "<<grid.options.support_type<<", lat_multiplier "<<grid.options.support_lat_multiplier<<" and lon_multiplier "<<grid.options.support_lon_multiplier<<endl;

      //Loop through input points- this is the outer loop because
      //the sep_vec- vector between the satellites, is calculated
      //only once per input time this way.
      notification.start_time = time(NULL);
      for(i=notification.starting_index;i<(long long)input.time.size();i++){

        notify(notification,i,least_squares,input,results);//Periodically print time remaining.

        //Each loop is a row (const input time)-column resets on each loop
        //Actually, zone of influence code makes this statement unnecessary
        //column=0;

        //Calculate sep_vec for this time.
        sep_vec.front() = input.xB.at(i) - input.xA.at(i);
        sep_vec.at(1) = input.yB.at(i) - input.yA.at(i);
        sep_vec.at(2) = input.zB.at(i) - input.zA.at(i);

        //Normalize sep_vec.
        distanceAB = sqrt(pow(sep_vec.front(),2)+pow(sep_vec.at(1),2)+pow(sep_vec.at(2),2));
        sep_vec.front() = sep_vec.front()/distanceAB;
        sep_vec.at(1) = sep_vec.at(1)/distanceAB;
        sep_vec.at(2) = sep_vec.at(2)/distanceAB;

        //Calculate coefficients that are parameter and time specific
        //but not tied to particular grid points.
        //Ex: constant's coefficient is just "1", trend is the input time
        //multiplied by a conversion factor to make the output in cm/year.
        //The type 101 parameters' coefficients are sin/cos(omega*t).
        //The result is a vector with output_type.size() entries- periods count twice!
        j=0;//Index of output vector - periods count twice.
        for(k=0;k<(long long)results.options.parameter.size();k++){
          //New parameter types should be added here.
          if(results.options.parameter_type.at(k)==101){//Harmonic
            param_coefs.at(j)=cos(results.omegas.at(k)*(input.time.at(i)));
            j++;
            param_coefs.at(j)=sin(results.omegas.at(k)*(input.time.at(i)));
            j++;
          }
          else if(results.options.parameter_type.at(k)==0){//Const
            param_coefs.at(j) = 1.0; j++;
          }
          else if(results.options.parameter_type.at(k)<100){//Polynomial
            //Because pow(±0,y) returns ±∞ and raises the divide-by-zero floating-point exception for y an odd integer.
            if(input.time.at(i) != 0) param_coefs.at(j)=pow((input.time.at(i))*inverse_seconds_in_year,results.options.parameter_type.at(k));//Partial of n'th polynomial term.
            else param_coefs.at(j)=0.0;//Won't ever return ±∞ or raise an exception.
            j++;
          }
          else if(results.options.parameter_type.at(k)>=10000 and results.options.parameter_type.at(k)<20000){//Arbitrary
            param_coefs.at(j) = input.arbitrary.y_values.at(k).at(0).at(i); j++;
          }
          else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.jfdi3mxkvh4e6"<<endl;
        }//End of loop through parameters.

        //Loop through grid- this is the next loop because this way
        //the distance from the input point(s) to each grid point is only
        //calculated once for each satellite.

        //2008-06-29 upgrade: zone_of_influence allows grid loop to only
        //bother calcing values if grid pt is "close enough" to input pt.
        long long current_input_binindex = input.regional_binindices.at(i);
        for(l=0;l<(long long)results.options.zone_of_influence.at(current_input_binindex).size();l++){
          j=results.options.zone_of_influence.at(current_input_binindex).at(l);
          //When using zone of influence, column needs to be set
          //to the right value for each new grid point in the zone so it
          //properly skips all the parameters for grid points not in zone.
          column = j*results.options.output_type.size();

          //Calculate vector/distance from grid point to input point A,B.
          //Convert from km to m.
          grid2A.front() = 1000.0*(input.xA.at(i) - grid.x.at(j));
          grid2A.at(1) = 1000.0*(input.yA.at(i) - grid.y.at(j));
          grid2A.at(2) = 1000.0*(input.zA.at(i) - grid.z.at(j));
          grid2B.front() = 1000.0*(input.xB.at(i) - grid.x.at(j));
          grid2B.at(1) = 1000.0*(input.yB.at(i) - grid.y.at(j));
          grid2B.at(2) = 1000.0*(input.zB.at(i) - grid.z.at(j));

          //Also calculate distances.  Already converted from km to m.
          distanceA = sqrt(pow(grid2A.front(),2)+pow(grid2A.at(1),2)+pow(grid2A.at(2),2));
          distanceB = sqrt(pow(grid2B.front(),2)+pow(grid2B.at(1),2)+pow(grid2B.at(2),2));

          //Now deriv of along-track acc w.r.t. mascon j's (current) mass.
          //Notice 1E9 factor that allows input accels to be in units of
          //nanometers/second^2.  Also notice the cm2kg conversion(grid() ).
          da_dmj = Big_G*1E9*grid.cm2kg.at(j)*((grid2A.front()*sep_vec.front()+grid2A.at(1)*sep_vec.at(1)+grid2A.at(2)*sep_vec.at(2))/pow(distanceA,3) - (grid2B.front()*sep_vec.front()+grid2B.at(1)*sep_vec.at(1)+grid2B.at(2)*sep_vec.at(2))/pow(distanceB,3));

          //Now weight the deriv of accel based on the support_weight
          //which takes into account the area that this main gr pt
          //covers.
          da_dmj *= grid.main_weights.at(j);

          //Now loop through support grid points.
          if((long long)grid.support_lat.at(j).size() > (long long)0) for(n=0;n<(long long)grid.support_lat.at(j).size();n++){
            //When using zone of influence, column needs to be reset
            //to the right value for each new support grid point so it
            //properly skips all the parameters for grid points not in zone.
            column = j*results.options.output_type.size();

            //Calculate vector/distance from supporting grid point to input
            //point A,B.  This is okay because the grid2A/B vecs not needed
            //anymore, and will be reset on the next main grid loop anyway.
            //Convert from km to m.
            grid2A.front()=1E3*(input.xA.at(i) - grid.support_x.at(j).at(n));
            grid2A.at(1)=1E3*(input.yA.at(i) - grid.support_y.at(j).at(n));
            grid2A.at(2)=1E3*(input.zA.at(i) - grid.support_z.at(j).at(n));
            grid2B.front()=1E3*(input.xB.at(i) - grid.support_x.at(j).at(n));
            grid2B.at(1)=1E3*(input.yB.at(i) - grid.support_y.at(j).at(n));
            grid2B.at(2)=1E3*(input.zB.at(i) - grid.support_z.at(j).at(n));

            //Also calculate distances.  Already converted from km to m.
            //Again, these aren't used anymore, and will be reset next loop.
            distanceA = sqrt(pow(grid2A.front(),2)+pow(grid2A.at(1),2)+pow(grid2A.at(2),2));
            distanceB = sqrt(pow(grid2B.front(),2)+pow(grid2B.at(1),2)+pow(grid2B.at(2),2));

            //Now deriv of along-track acc w.r.t. mascon j's (current) mass.
            //Notice 1E9 factor that allows input accels to be in units of
            //nm/sec^2.  Also notice the cm2kg conversion(grid() ).
            //Also note weight which takes into account the area that this
            //support gr pt covers.
            da_dmj += grid.support_weights.at(j).at(n)*Big_G*1E9*grid.cm2kg.at(j)*((grid2A.front()*sep_vec.front()+grid2A.at(1)*sep_vec.at(1)+grid2A.at(2)*sep_vec.at(2))/pow(distanceA,3) - (grid2B.front()*sep_vec.front()+grid2B.at(1)*sep_vec.at(1)+grid2B.at(2)*sep_vec.at(2))/pow(distanceB,3));

          }//End of loop through supporting grid points.

          //Now loop through all params, const, trend, sine, cosine
          //to obtain this gr pt's part of the current row of the H matrix
          //(stored in kvector.x_vector) and the contribution to the b
          //matrix from this grid point's part of this row.
          for(k=0;k<(long long)results.options.output_type.size();k++){
            //Calc the val of H in the i'th row and column'th column.
            //where column = results.options.output_type.size()*j
            //where j = current gr pt in the zone of influence.
            gsl_vector_set(kvector.x_vector, column,da_dmj*param_coefs.at(k));

            //For the b vector, must multiply kvector.x_vector by
            //input.accel.at(i).
            gsl_vector_set(least_squares.b, column, gsl_vector_get(least_squares.b,column) + gsl_vector_get(kvector.x_vector, column)*input.accel.at(i));

            column++;//Another parameter for this grid point.
          }//End of loop through parameters.

          //Now that all parameters for this grid point have been added to
          //the x_vector, the contribution for the A matrix from this grid
          //point's parameters can be calculated.

          //Loop through all the grid points in the current zone of
          //influence for this input point that have already been
          //calculated, including the current one to calculate correlations
          //between different parameters of differen grid points (entries
          //in the A matrix, in other words.)
          //Don't loop through points that haven't already been calced
          //because the contributions from those points will be added when
          //they are calculated later in the big loop through zone of influ.
          for(long long tik=0;tik <= l;tik++){
            //1st, reset column so it starts over at beginning of param list
            //for current gr pt.
            column = j*results.options.output_type.size();

            //Obtain grid index for this previous point which is also in
            //the zone of influence
            long long new_j=results.options.zone_of_influence.at(current_input_binindex).at(tik);

            //Now loop through parameters of current grid point, where
            //position in the x_vector is kept in "column"
            for(k=0;k<(long long)results.options.output_type.size();k++){
              //This is the index in x_vector where the previous grid pt's
              //parameters begin.
              long long new_column = new_j*results.options.output_type.size();

              //Now loop through parameters of previous grid point, where
              //position in the x_vector is kept in "new_column"
              for(m=0;m<(long long)results.options.output_type.size();m++){

                //if(i==0) cout<<"Incrementing value of A matrix at 1st index new_column="<<new_column<<" and 2nd index "<<column<<" with new value "<<gsl_vector_get(kvector.x_vector, new_column)*gsl_vector_get(kvector.x_vector, column)<<endl;
                gsl_matrix_set(least_squares.A, new_column, column,gsl_matrix_get(least_squares.A,new_column,column) + gsl_vector_get(kvector.x_vector, new_column)*gsl_vector_get(kvector.x_vector, column));
                new_column++;//Increment the OTHER gr pt's parameter.
              }//End of loop through previous grid point parameters.
              column++;//Increment the current gr pt's parameter.
            }//End of loop through current grid point parameters.
          }//End of loop through gr pts in zone that are already computed.
        }//End of loop through grid points in zone of influence.
      }//End of loop through input.

      //Now free memory taken up by Kahan temp vector.
      gsl_vector_free(kvector.x_vector);

      //Multiply "b" by 1E9 to convert "y" from meters to nanometers.
      gsl_blas_dscal((double)1E9, least_squares.b);

      mirror_across_diagonal(least_squares,results);

      break;
    }
    ///////////////////////////////////////////////////////////////////////
    //Case 202- remember to cp to perform_upward_continuation,ls_cache_init
    ///////////////////////////////////////////////////////////////////////
    case 202:{
      //GSL format, pt masses. Sames as 201 but uses STL vecs in popultn.

      cout<<results.options.prefix<<": Inverting (STL multivector population) using supporting grid of type "<<grid.options.support_type<<", lat_multiplier "<<grid.options.support_lat_multiplier<<" and lon_multiplier "<<grid.options.support_lon_multiplier<<endl;
      
      //A faster replacement for x_vector.
      least_squares.stl_x.clear();
      least_squares.stl_x.resize(least_squares.dimension,0.0);

      //Will this speed up program, or does -O2 take care of this anyway?
      long long output_size = results.options.output_type.size();

      //Loop through input points- this is the outer loop because
      //the sep_vec- vector between the satellites, is calculated
      //only once per input time this way.
      notification.start_time = time(NULL);
      for(i=notification.starting_index;i<(long long)input.time.size();i++){

        notify(notification,i,least_squares,input,results);//Periodically print time remaining.

        //Only look for nonzero arbitrary values if that's requested.
        int nonzero = 1;
        if(1 == 2){
          nonzero=0;//Assume no nonzero values.
          for(k=0;k<(long long)input.arbitrary.y_values.size();k++){
            //Not arbitrary unless this plot has at least 1 line.
            if((long long)input.arbitrary.y_values.at(k).size()>0){
              if(input.arbitrary.y_values.at(k).at(0).at(i) != 0.0) nonzero = 1;
            }
          }
        }
        if(nonzero == 1){
          //Calculate sep_vec for this time.
          sep_vec[0] = input.xB[i] - input.xA[i];
          sep_vec[1] = input.yB[i] - input.yA[i];
          sep_vec[2] = input.zB[i] - input.zA[i];

          //Normalize sep_vec.
          distanceAB = sqrt(pow(sep_vec[0],2)+pow(sep_vec[1],2)+pow(sep_vec[2],2));
          sep_vec[0] = sep_vec[0]/distanceAB;
          sep_vec[1] = sep_vec[1]/distanceAB;
          sep_vec[2] = sep_vec[2]/distanceAB;

          //Calculate coefficients that are parameter and time specific
          //but not tied to particular grid points.
          //Ex: constant's coefficient is just "1", trend is the input time
          //multiplied by a conversion factor to make the output in cm/year.
          //The type 101 parameters' coefficients are sin/cos(omega*t).
          //The result is a vector with output_type.size() entries- periods count twice!
          j=0;//Index of output vector - periods count twice.
          for(k=0;k<(long long)results.options.parameter.size();k++){
            //New parameter types should be added here.
            if(results.options.parameter_type[k]==101){//Harmonic
              double current_phase = results.omegas[k]*(input.time[i]);
              param_coefs[j]=cos(current_phase);
              j++;
              param_coefs[j]=sin(current_phase);
              j++;
            }
            else if(results.options.parameter_type[k]==0){//Const
              param_coefs[j] = 1.0; j++;
            }
            else if(results.options.parameter_type[k]<100){//Polynomial
              //Because pow(±0,y) returns ±∞ and raises the divide-by-zero floating-point exception for y an odd integer.
              if(input.time[i] != 0) param_coefs[j]=pow((input.time[i])*inverse_seconds_in_year,results.options.parameter_type[k]);//Partial of n'th polynomial term.
              else param_coefs[j]=0.0;//Won't ever return ±∞ or raise an exception.
              j++;
            }
            else if(results.options.parameter_type[k]>=10000 and results.options.parameter_type[k]<20000){//Arbitrary
              param_coefs[j] = input.arbitrary.y_values[k][0][i]; j++;
            }
            else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type[k]<<" was not recognized.jfdi3mxkvh4e6"<<endl;
          }//End of loop through parameters.

          //Loop through grid- this is the next loop because this way
          //the distance from the input point(s) to each grid point is only
          //calculated once for each satellite.

          //2008-06-29 upgrade: zone_of_influence allows grid loop to only
          //bother calcing values if grid pt is "close enough" to input pt.
          long long current_input_binindex = input.regional_binindices[i];
          for(l=0;l<(long long)results.options.zone_of_influence[current_input_binindex].size();l++){
            j=results.options.zone_of_influence[current_input_binindex][l];
            //When using zone of influence, column needs to be set
            //to the right value for each new grid point in the zone so it
            //properly skips all the parameters for grid points not in zone.
            column = j*output_size;

            //Calculate vector/distance from grid point to input point A,B.
            //Convert from km to m.
            grid2A[0] = 1000.0*(input.xA[i] - grid.x[j]);
            grid2A[1] = 1000.0*(input.yA[i] - grid.y[j]);
            grid2A[2] = 1000.0*(input.zA[i] - grid.z[j]);
            grid2B[0] = 1000.0*(input.xB[i] - grid.x[j]);
            grid2B[1] = 1000.0*(input.yB[i] - grid.y[j]);
            grid2B[2] = 1000.0*(input.zB[i] - grid.z[j]);

            //Also calculate distances.  Already converted from km to m.
            distanceA = sqrt(pow(grid2A[0],2)+pow(grid2A[1],2)+pow(grid2A[2],2));
            distanceB = sqrt(pow(grid2B[0],2)+pow(grid2B[1],2)+pow(grid2B[2],2));

            //Now deriv of along-track acc w.r.t. mascon j's (current) mass.
            //Notice 1E9 factor that allows input accels to be in units of
            //nanometers/second^2.  Also notice cm2kg conversion(grid() ).
            da_dmj = Big_G*1E9*grid.cm2kg[j]*((grid2A[0]*sep_vec[0]+grid2A[1]*sep_vec[1]+grid2A[2]*sep_vec[2])/pow(distanceA,3) - (grid2B[0]*sep_vec[0]+grid2B[1]*sep_vec[1]+grid2B[2]*sep_vec[2])/pow(distanceB,3));

            //Now weight the deriv of accel based on the support_weight
            //which takes into account the area that this main gr pt
            //covers.
            da_dmj *= grid.main_weights[j];

            //Now loop through support grid points.
            if((long long)grid.support_lat[j].size() > (long long)0) for(n=0;n<(long long)grid.support_lat[j].size();n++){
              //When using zone of influence, column needs to be reset
              //to the right value for each new support grid point so it
              //properly skips all the params for grid points not in zone.
              column = j*output_size;

              //Calculate vector/distance from supporting grid point to input
              //point A,B.  This is okay because the grid2A/B vecs not needed
              //anymore, and will be reset on the next main grid loop anyway.
              //Convert from km to m.
              grid2A[0]=1E3*(input.xA[i] - grid.support_x[j][n]);
              grid2A[1]=1E3*(input.yA[i] - grid.support_y[j][n]);
              grid2A[2]=1E3*(input.zA[i] - grid.support_z[j][n]);
              grid2B[0]=1E3*(input.xB[i] - grid.support_x[j][n]);
              grid2B[1]=1E3*(input.yB[i] - grid.support_y[j][n]);
              grid2B[2]=1E3*(input.zB[i] - grid.support_z[j][n]);

              //Also calculate distances.  Already converted from km to m.
              //Again,these aren't used anymore,and will be reset next loop.
              distanceA = sqrt(pow(grid2A[0],2)+pow(grid2A[1],2)+pow(grid2A[2],2));
              distanceB = sqrt(pow(grid2B[0],2)+pow(grid2B[1],2)+pow(grid2B[2],2));

              //Now deriv of along-track acc w.r.t. mscn j's (current) mass.
              //Notice 1E9 factor that allows input accels to be in units of
              //nm/sec^2.  Also notice the cm2kg conversion(grid() ).
              //Also note weight which takes into account the area that this
              //support gr pt covers.
              da_dmj += grid.support_weights[j][n]*Big_G*1E9*grid.cm2kg[j]*((grid2A[0]*sep_vec[0]+grid2A[1]*sep_vec[1]+grid2A[2]*sep_vec[2])/pow(distanceA,3) - (grid2B[0]*sep_vec[0]+grid2B[1]*sep_vec[1]+grid2B[2]*sep_vec[2])/pow(distanceB,3));

            }//End of loop through supporting grid points.

            //Now loop through all params, const, trend, sine, cosine
            //to obtain this gr pt's part of the current row of the H matrix
            //(stored in ls.stl_x, called x_vector) and the contrib to the b
            //matrix from this grid point's part of this row.
            for(k=0;k<output_size;k++){
              //Calc the val of H in the i'th row and column'th column.
              //where column = output_size*j
              //where j = current gr pt in the zone of influence.
              double speedtemp=da_dmj*param_coefs[k];
              least_squares.stl_x[column] = speedtemp;

              //For the b vector, must multiply x_vector by input.accel[i].
              least_squares.stl_b[column] += speedtemp*input.accel[i];

              column++;//Another parameter for this grid point.
            }//End of loop through parameters.

            //Now that all parameters for this grid point have been added to
            //the x_vector, the contribution for the A matrix from this grid
            //point's parameters can be calculated.

            //Loop through all the grid points in the current zone of
            //influence for this input point that have already been
            //calculated, including the current one to calculate correlations
            //between different parameters of differen grid points (entries
            //in the A matrix, in other words.)
            //Don't loop through points that haven't already been calced
            //because the contributions from those points will be added when
            //they are calculated later in the big loop thru zone of influ.
            for(long long tik=0;tik <= l;tik++){
              //1st, reset column so it starts over at beginng of param list
              //for current gr pt.
              column = j*output_size;

              //Obtain grid index for this previous point which is also in
              //the zone of influence
              long long new_j=results.options.zone_of_influence[current_input_binindex][tik];

              //Now loop through parameters of current grid point, where
              //position in the x_vector is kept in "column"
              for(k=0;k<output_size;k++){
                double current_x = least_squares.stl_x[column];
                //This is the index in x_vector where the previous grid pt's
                //parameters begin.
                long long new_column = new_j*output_size;

                //Now loop through parameters of previous grid point, where
                //position in the x_vector is kept in "new_column"
                for(m=0;m<output_size;m++){

                  //if(i==0) cout<<"Incrementing value of A matrix at 1st index new_column="<<new_column<<" and 2nd index "<<column<<" with new value "<<least_squares.stl_x[new_column]*least_squares.stl_x[column]<<endl;
                  least_squares.stl_A[new_column][column] += least_squares.stl_x[new_column]*current_x;
                  new_column++;//Increment the OTHER gr pt's parameter.
                }//End of loop through previous grid point parameters.
                column++;//Increment the current gr pt's parameter.
              }//End of loop through current grid point parameters.
            }//End of loop through gr pts in zone that are already computed.
          }//End of loop through grid points in zone of influence.
        }//End of "if arbitrary params aren't all zero."
      }//End of loop through input.

      //Multiply "b" by 1E9 to convert "y" from meters to nanometers.
      scale_b_vector(least_squares,results,(double)1E9);

      //Save A,b to cache, then clear stl vectors.
      //The region is finished, and p_p_s() subtracts 1 from i
      //automatically.
      i=input.time.size();
      perform_partial_save(least_squares,input,results,i);
      least_squares.stl_b.clear();
      least_squares.stl_x.clear();
      least_squares.stl_A.clear();

      break;
    }
  }//End of switch-case

  //Cases such as 202 just pop'd in stl, so load from file.
  if(results.options.already_moved_to_cache==1){
    //Ironically, this needs to be set to 0 to load into gsl matrices.
    results.options.already_moved_to_cache=0;
    cout<<results.options.prefix<<": Reloading just-finished matrices from cache..."<<endl;
    least_squares_load_from_cache(least_squares, results,grid);
    cout<<"Finished."<<endl;
    //But it needs to be reset, otherwise cls_parallel will save it again
    results.options.already_moved_to_cache=1;
  }

  notification.end_time = time(NULL);//Record time at end of program.
  notification.time_total=notification.end_time-notification.start_time;
  notification.timespan_conversion.seconds=notification.time_total;
  //cout<<results.options.prefix<<" population (IN THE INNER FUNCTION! SO POSSIBLY JUST A PARALLEL CHUNK OF THE OVERALL POPULATION!) took"<<sec2human(timespan_conversion)<<endl;

  cout<<results.options.prefix<<": Population took "<<sec2human(notification.timespan_conversion)<<endl;
  #endif
}

least_squares_s regularize(grid_s &grid,
                           results_s &results){
  /**********************************************************************
  Purpose: This function creates the regularization matrix (gaussian
          smoothing, Tikhonov regularization, etc) to be added to the ls.A
          matrix before solving.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  least_squares_s least_squares;

  //Requires GSL libraries.
  #ifdef GSL_HERE
  long long i,j,k;
  double temp_double=0,current_distance,normalization;

  //These variables are used to display run time.
  time_t start_time,end_time,time_total;
  timespan_conversion_s timespan_conversion;

  start_time = time(NULL);//Record time at start of program.

  //Same dimensions as A=H^T*H
  least_squares.dimension = (long long)grid.lat.size()*results.options.output_type.size();

  switch(results.options.regularization_choice){
    default: cout<<"!!!!WARNING!!!!!! results.options.regularization_choice "<<results.options.regularization_choice<<" isn't recognized."<<endl;
    case 0:{//Symmetric Gaussian smoothing.
      least_squares.reg_title = "Symmetric Gaussian";

      //Initialize "F" matrix- correct dimensions and populated with 0.0.
      least_squares.F = gsl_matrix_alloc(least_squares.dimension, least_squares.dimension);
      gsl_matrix_set_zero(least_squares.F);

      //Compute the symmetric matrix F.
      cout<<results.options.prefix<<" is populating Gaussian regularization matrix."<<endl;
      for(i=0;i<(long long)grid.lat.size();i++){
        //Normalize gaussian fctn by summing gaussian vals at all grid pts.
        normalization=0.0;
        for(j=0;j<(long long)grid.lat.size();j++){
          if(j != i){
            current_distance = planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j));
            normalization += -exp(-pow(current_distance,2)/(2*pow(results.options.regparam1,2)));
          }
        }

        //Calculate gauss values at all grid pair combos for F matrix.
        //Loop through all grid points greater than pt i- the OTHER grid pts
        for(j=i;j<(long long)grid.lat.size();j++){
          //Depends on grid geometry and gaussian smoothing halfwidth.
          //Only bother to calc gaussian values for non-diag elements.
          if(j != i){
            current_distance = planet_avg_radius*spherical_distance(grid.lat.at(i),grid.lon.at(i),grid.lat.at(j),grid.lon.at(j));
            temp_double = -exp(-pow(current_distance,2)/(2*pow(results.options.regparam1,2)));
          }
          //Now loop through all params, const, trend, sine, cosine..
          //Note that all params for a particular gr pt have same value.
          for(k=0;k<(long long)results.options.output_type.size();k++){
            //Diagonal:
            if(j==i){
              gsl_matrix_set(least_squares.F, i*results.options.output_type.size()+k, j*results.options.output_type.size()+k,1.0);
            }
            //Non-diagonal:
            else{
              //First location:
              gsl_matrix_set(least_squares.F, i*results.options.output_type.size()+k, j*results.options.output_type.size()+k,temp_double);
              //Second location- reversing order of gr pts, on other side
              //of the diagonal:
              gsl_matrix_set(least_squares.F, j*results.options.output_type.size()+k, i*results.options.output_type.size()+k,temp_double);
            }
          }//End of loop through parameters.
        }//End of loop through OTHER grid pts.
      }//End of loop through grid pts.

      //Initialize "R" matrix- correct dimensions and populated with 0.0.
      least_squares.R = gsl_matrix_alloc(least_squares.dimension, least_squares.dimension);
      gsl_matrix_set_zero(least_squares.R);

      //R = F^T*F
      gsl_blas_dgemm(CblasTrans, CblasNoTrans, 1.0, least_squares.F, least_squares.F, 0.0, least_squares.R);

      //Don't need F anymore.
      gsl_matrix_free(least_squares.F);

      break;
    }
    case 1:{//Tikhonov regularization.
      least_squares.reg_title = "Tikhonov";
      //Compute the identity matrix R.
      cout<<results.options.prefix<<": Populating Tikhonov regularization matrix."<<endl;

      //Initialize "R" matrix- correct dimensions and populated with 0.0.
      least_squares.R = gsl_matrix_alloc(least_squares.dimension, least_squares.dimension);
      gsl_matrix_set_identity(least_squares.R);

      break;
    }
  }//End of switch-case

  end_time = time(NULL);//Record time at end of program.
  time_total = end_time - start_time;
  timespan_conversion.seconds=time_total;
  if((long long)time_total > 60) cout<<results.options.prefix<<" regularization matrix population took"<<sec2human(timespan_conversion)<<endl;
  #endif
  return least_squares;
}

void regularize_in_place(grid_s &grid,
                         results_s &results,
                         least_squares_s &least_squares){
  /**********************************************************************
  Purpose: This function adds the regularization matrix (gaussian
          smoothing, Tikhonov regularization, etc) to the ls.A matrix
          in place (to reduce memory requirements).
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE
  long long i,j,k;
  //double temp_double=0,current_distance,normalization;

  //These variables are used to display run time.
  time_t start_time,end_time,time_total;
  timespan_conversion_s timespan_conversion;

  start_time = time(NULL);//Record time at start of program.

  //Same dimensions as A=H^T*H
  least_squares.dimension = (long long)grid.lat.size()*results.options.output_type.size();

  switch(results.options.regularization_choice){
    default: cout<<"!!!!WARNING!!!!!! results.options.regularization_choice "<<results.options.regularization_choice<<" isn't recognized."<<endl;
    case 0:{//Symmetric Gaussian smoothing.
      cout<<"!!!!WARNING!!!!!! results.options.regularization_choice "<<results.options.regularization_choice<<" isn't implemented in_place yet!"<<endl;
      break;
    }
    case 1:{//Tikhonov regularization.
      least_squares.reg_title = "Tikhonov";
      cout<<results.options.prefix<<": Adding Tikhonov regularization matrix in-place."<<endl;

      for(i=0;i<(long long)grid.lat.size();i++){
        j=0;//Holds the position in the output vector.
        for(k=0;k<(long long)results.options.parameter.size();k++){
          //New parameter types should be added here.
          if(results.options.parameter_type.at(k) < 100){
            gsl_matrix_set(least_squares.A, i*results.options.output_type.size()+j, i*results.options.output_type.size()+j,results.options.lambda+gsl_matrix_get(least_squares.A, i*results.options.output_type.size()+j, i*results.options.output_type.size()+j));
            j++;
          }
          else if(results.options.parameter_type.at(k) == 101){
            gsl_matrix_set(least_squares.A, i*results.options.output_type.size()+j, i*results.options.output_type.size()+j,results.options.lambda+gsl_matrix_get(least_squares.A, i*results.options.output_type.size()+j, i*results.options.output_type.size()+j));
            j++;
            gsl_matrix_set(least_squares.A, i*results.options.output_type.size()+j, i*results.options.output_type.size()+j,results.options.lambda+gsl_matrix_get(least_squares.A, i*results.options.output_type.size()+j, i*results.options.output_type.size()+j));
            j++;
          }
          else if(results.options.parameter_type.at(k) >= 10000 and results.options.parameter_type.at(k) < 20000){
            gsl_matrix_set(least_squares.A, i*results.options.output_type.size()+j, i*results.options.output_type.size()+j,results.options.lambda+gsl_matrix_get(least_squares.A, i*results.options.output_type.size()+j, i*results.options.output_type.size()+j));
            j++;
          }
          else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.4"<<endl;
        }//End of loop through parameters.
      }//End of loop through grid pts.

      break;
    }
  }//End of switch-case

  end_time = time(NULL);//Record time at end of program.
  time_total = end_time - start_time;
  timespan_conversion.seconds=time_total;
  if((long long)time_total > 60) cout<<results.options.prefix<<" in-place regularization took"<<sec2human(timespan_conversion)<<endl;
  #endif
}

double nearest_topo_pt_elev(double current_lat,
                            double current_lon,
                            ancillary_s &ancillary){
  /**********************************************************************
  Purpose: This function finds the nearest topography pt in the ancillary
          structure to (current_lat,current_lon) and returns its elevation
          in meters, with negative elevations meaning depth below sea level.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)

          double - Elevation at nearest topo pt, in meters(neg = ocn depth)
  **********************************************************************/
  double current_angle,smallest_distance;
  double smallest_angle=1000.0;//Need to reset the smallest angle.
  long long k,smallest_k=-1;

  for(k=0;k<(long long)ancillary.topo.lat.size();k++){
    current_angle = spherical_distance(current_lat,current_lon,
                      ancillary.topo.lat.at(k),ancillary.topo.lon.at(k));
    if(current_angle < smallest_angle){
      smallest_angle = current_angle;
      smallest_k = k;
    }
  }//End of loop though topo.
  if(smallest_k<0) cout<<"nearest_topo_pt_elev() failed- no topo pt was close to point with lat "<<current_lat<<" and lon "<<current_lon<<endl;
  //Check to make sure that the nearest topo point is "near enough".
  smallest_distance = smallest_angle*planet_avg_radius;
  if(smallest_distance > max_topo_res){
    printf("!!!WARNING!!! Point at (%4.1f,%5.1f) is %.0f km from topo pt (%4.1f,%5.1f), elev=%.0f m.\n", current_lat, current_lon, smallest_distance, ancillary.topo.lat.at(smallest_k), ancillary.topo.lon.at(smallest_k), ancillary.topo.elev.at(smallest_k)); cout<<endl;
  }

  return ancillary.topo.elev.at(smallest_k);
}

void create_output_vectors(results_s &results, input_s &input){
  /**********************************************************************
  Purpose: This function uses results to push_back() titles
          in titles and units.
          More importantly, it also push_backs new entries in the
          output_type and output_tide vectors based on period_info from a
          run of is_this_period_recognized() and
          results.options.sincos (which needs to be set BEFORE
          this function is called).
          Input structure is necessary because of the arbitrary
          parameter structure inside it.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  char str[max_length];
  period_info_s period_info;

  //If this function is called, assume any previous vectors aren't needed.
  results.titles.clear();
  results.units.clear();
  results.outputs.clear();
  results.options.output_type.clear();
  results.options.output_tide.clear();
  results.options.parameter_tide.clear();

  //Calculate angular frequencies corresponding to any/all periods.
  results.omegas.clear();
  results.omegas.resize(results.options.parameter.size(),0.0);
  for(long long i=0;i<(long long)results.options.parameter.size();i++){
    if(results.options.parameter_type.at(i) == 101){
      if(results.options.parameter.at(i)==0.0) cout<<"WARNING!!!! Parameter #"<<i+1<<" was zero, but it should hold a sinusoid period in seconds!"<<endl;
      results.omegas.at(i) = twoPi/results.options.parameter.at(i);
    }
  }

  //Control units here based on analysis type.
  if(results.options.type >= 2 and results.options.type <= 4) results.base_unit = "nm/s^2";
  else results.base_unit = "cm";
  string polybase = results.base_unit+"/year";

  for(long long k=0;k<(long long)results.options.parameter.size();k++){
    //New parameter types should be added here.
    if(results.options.parameter_type.at(k) == 0){
      results.titles.push_back("Constant Term");
      results.units.push_back(results.base_unit);
      results.options.output_type.push_back(0);
      results.options.parameter_tide.push_back(0);
      results.options.output_tide.push_back(0);
    }
    else if(results.options.parameter_type.at(k) == 1){
      results.titles.push_back("Secular Trend");
      results.units.push_back(polybase);
      results.options.output_type.push_back(results.options.parameter_type.at(k));
      results.options.parameter_tide.push_back(0);
      results.options.output_tide.push_back(0);
    }
    else if(results.options.parameter_type.at(k) == 2){
      results.titles.push_back("Acceleration Term");
      sprintf(str,"%s^%2d",polybase.c_str(),results.options.parameter_type.at(k));
      results.units.push_back(str);
      results.options.output_type.push_back(results.options.parameter_type.at(k));
      results.options.parameter_tide.push_back(0);
      results.options.output_tide.push_back(0);
    }
    else if(results.options.parameter_type.at(k) < 100){
      sprintf(str,"Time^%2d Term",results.options.parameter_type.at(k));
      results.titles.push_back(str);
      sprintf(str,"%s^%2d",polybase.c_str(),results.options.parameter_type.at(k));
      results.units.push_back(str);
      results.options.output_type.push_back(results.options.parameter_type.at(k));
      results.options.parameter_tide.push_back(0);
      results.options.output_tide.push_back(0);
    }
    else if(results.options.parameter_type.at(k) == 101){
      is_this_period_recognized(results.options.parameter.at(k),period_info);
      strcpy(str,period_info.title.c_str());
      results.titles.push_back(str);
      results.units.push_back(results.base_unit);
      results.options.parameter_tide.push_back(period_info.tide);
      results.options.output_tide.push_back(period_info.tide);
      results.options.output_tide.push_back(period_info.tide);
      //Set 2 sinusoid titles and units depending on type of analysis.
      switch(results.options.sincos){
        default: cout<<"!!!!WARNING!!!!!! results.options.sincos "<<results.options.sincos<<" isn't recognized."<<endl;
        case 0:{
          //First set.
          results.titles.back().append(" Amplitude");
          results.options.output_type.push_back(103);
          //Second set.
          results.titles.push_back(str);
          results.titles.back().append(" Phase");
          results.units.push_back("degrees");
          results.options.output_type.push_back(104);
          break;
        }
        case 1:{
          //First set.
          results.titles.back().append(" Cosine Coefficient");
          results.options.output_type.push_back(101);
          //Second set.
          results.titles.push_back(str);
          results.titles.back().append(" Sine Coefficient");
          results.units.push_back("cm");
          results.options.output_type.push_back(102);
          break;
        }
      }//End switch-case.
    }//End of "if this parameter is a sinusoid" if statement.
    else if(results.options.parameter_type.at(k) >= 10000 and results.options.parameter_type.at(k) < 20000){
      results.titles.push_back(input.arbitrary.titles.at(k));
      results.units.push_back(input.arbitrary.y_units.at(k));
      results.options.output_type.push_back(results.options.parameter_type.at(k));
      results.options.parameter_tide.push_back(0);
      results.options.output_tide.push_back(0);
    }
    else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.s"<<endl;
  }

  //Resize first dimension of outputs so it can hold all parameters,
  //where periods count twice. Second dimension will be set in
  //define_grid() because each parameter has grid.lat.size() values.
  results.outputs.resize(results.options.output_type.size());
}

void is_this_period_recognized(double period, period_info_s &period_info){
  /**********************************************************************
  Purpose: This function examines the period (in seconds) passed to it,
          checking to see if it is within "sloppy_local_tolerance" of
          the various periods defined in definitions.hpp.

          If not, it sets period_info.recognized = 0. The ang. freq.
          is calculated, the period is assumed not to be a tide, and
          a custom title is printed with this unrecognized period.

          If so, it sets period_info.recognized = 1 and records the
          period (in seconds), omega (twoPi/period in seconds), and title.
  ***********************************************************************
  Input: (types ending in "_s" are defined in definitions.hpp)
         double period - Period in question, in seconds.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  char str[max_length];

  period_info.recognized = 1;//If not recognized, this is set to 0.
  //Ever since I changed periods to seconds, tolerance should be 2 seconds.
  double sloppy_local_tolerance = 2;

  period_info.parameter_type = 101;//This is definitely a sinusoid!

  //Initialize Doodson numbers so non-tides have correctly sized vector.
  period_info.doodson.numbers.clear();
  period_info.doodson.numbers.resize(7,0);

  period_info.doodson.h2kj_sign = 1;//Preset to 1.

  if(fabs(period-m2_period)<=sloppy_local_tolerance){
    sprintf(str,"M2");
    period_info.period = m2_period;
    period_info.omega = m2_omega;
    period_info.tide = 1;
    int d_nums[]={2,0,0,0,0,0}; cp_doodson(d_nums, period_info);
  }
  else if(fabs(period-m4_period)<=sloppy_local_tolerance){
    sprintf(str,"M4");
    period_info.period = m4_period;
    period_info.omega = m4_omega;
    period_info.tide = 1;
    int d_nums[]={4,0,0,0,0,0}; cp_doodson(d_nums, period_info);
  }
  else if(fabs(period-m2_plus_period)<=sloppy_local_tolerance){
    sprintf(str,"M2 + 1 cycle/year");
    period_info.period = m2_plus_period;
    period_info.omega = m2_plus_omega;
    period_info.tide = 0;//Technically not a tide- it's modulated by ann ice
  }
  else if(fabs(period-m2_minus_period)<=sloppy_local_tolerance){
    sprintf(str,"M2 - 1 cycle/year");
    period_info.period = m2_minus_period;
    period_info.omega = m2_minus_omega;
    period_info.tide = 0;//Technically not a tide- it's modulated by ann ice
  }
  else if(fabs(period-k1_period)<=sloppy_local_tolerance){
    sprintf(str,"K1");
    period_info.period = k1_period;
    period_info.omega = k1_omega;
    period_info.tide = 1;
    int d_nums[]={1,1,0,0,0,0}; cp_doodson(d_nums, period_info);
  }
  else if(fabs(period-s2_period)<=sloppy_local_tolerance){
    sprintf(str,"S2");
    period_info.period = s2_period;
    period_info.omega = s2_omega;
    period_info.tide = 1;
    int d_nums[]={2,2,-2,0,0,0}; cp_doodson(d_nums, period_info);
  }
  else if(fabs(period-s1_period)<=sloppy_local_tolerance){
    sprintf(str,"S1");
    period_info.period = s1_period;
    period_info.omega = s1_omega;
    period_info.tide = 1;
    int d_nums[]={1,1,-1,0,0,1}; cp_doodson(d_nums, period_info);
  }
  else if(fabs(period-o1_period)<=sloppy_local_tolerance){
    sprintf(str,"O1");
    period_info.period = o1_period;
    period_info.omega = o1_omega;
    period_info.tide = 1;
    int d_nums[]={1,-1,0,0,0,0}; cp_doodson(d_nums, period_info);
    period_info.doodson.h2kj_sign = -1;
  }
  else if(fabs(period-p1_period)<=sloppy_local_tolerance){
    sprintf(str,"P1");
    period_info.period = p1_period;
    period_info.omega = p1_omega;
    period_info.tide = 1;
    int d_nums[]={1,1,-2,0,0,0}; cp_doodson(d_nums, period_info);
    period_info.doodson.h2kj_sign = -1;
  }
  else if(fabs(period-n2_period)<=sloppy_local_tolerance){
    sprintf(str,"N2");
    period_info.period = n2_period;
    period_info.omega = n2_omega;
    period_info.tide = 1;
    int d_nums[]={2,-1,0,1,0,0}; cp_doodson(d_nums, period_info);
  }
  else if(fabs(period-two_n2_period)<=sloppy_local_tolerance){
    sprintf(str,"2N2");
    period_info.period = two_n2_period;
    period_info.omega = two_n2_omega;
    period_info.tide = 1;
    int d_nums[]={2,-2,0,2,0,0}; cp_doodson(d_nums, period_info);
  }
  else if(fabs(period-k2_period)<=sloppy_local_tolerance){
    sprintf(str,"K2");
    period_info.period = k2_period;
    period_info.omega = k2_omega;
    period_info.tide = 1;
    int d_nums[]={2,2,0,0,0,0}; cp_doodson(d_nums, period_info);
  }
  else if(fabs(period-mf_period)<=sloppy_local_tolerance){
    sprintf(str,"Mf");
    period_info.period = mf_period;
    period_info.omega = mf_omega;
    period_info.tide = 1;
    int d_nums[]={0,2,0,0,0,0}; cp_doodson(d_nums, period_info);
    period_info.doodson.h2kj_sign = -1;
  }
  else if(fabs(period-q1_period)<=sloppy_local_tolerance){
    sprintf(str,"Q1");
    period_info.period = q1_period;
    period_info.omega = q1_omega;
    period_info.tide = 1;
    int d_nums[]={1,-2,0,1,0,0}; cp_doodson(d_nums, period_info);
    period_info.doodson.h2kj_sign = -1;
  }
  else if(fabs(period-lunar_sidereal_period)<=sloppy_local_tolerance){
    sprintf(str,"Lunar Sidereal");
    period_info.period = lunar_sidereal_period;
    period_info.omega = lunar_sidereal_omega;
    period_info.tide = 1;
    int d_nums[]={0,0,0,0,0,0}; cp_doodson(d_nums, period_info);
  }
  else if(fabs(period-semiannual_period)<=sloppy_local_tolerance){
    sprintf(str,"Semi-Annual");
    period_info.period = semiannual_period;
    period_info.omega = semiannual_omega;
    period_info.tide = 0;
  }
  else if(fabs(period-annual_period)<=sloppy_local_tolerance){
    sprintf(str,"Annual");
    period_info.period = annual_period;
    period_info.omega = annual_omega;
    period_info.tide = 0;
  }
  else if(fabs(period-four_year_period)<=sloppy_local_tolerance){
    sprintf(str,"4 Year");
    period_info.period = four_year_period;
    period_info.omega = four_year_omega;
    period_info.tide = 0;
  }
  else if(fabs(period-mm_period)<=sloppy_local_tolerance){
    sprintf(str,"Mm");
    period_info.period = mm_period;
    period_info.omega = mm_omega;
    period_info.tide = 1;
    int d_nums[]={0,1,0,-1,0,0}; cp_doodson(d_nums, period_info);
    period_info.doodson.h2kj_sign = -1;
  }
  else if(fabs(period-msqm_period)<=sloppy_local_tolerance){
    sprintf(str,"Msqm");
    period_info.period = msqm_period;
    period_info.omega = msqm_omega;
    period_info.tide = 1;
    int d_nums[]={0,4,-2,0,0,0}; cp_doodson(d_nums, period_info);
    period_info.doodson.h2kj_sign = -1;//Probably true?
  }
  else if(fabs(period-mtm_period)<=sloppy_local_tolerance){
    sprintf(str,"Mtm");
    period_info.period = mtm_period;
    period_info.omega = mtm_omega;
    period_info.tide = 1;
    int d_nums[]={0,3,0,-1,0,0}; cp_doodson(d_nums, period_info);
    period_info.doodson.h2kj_sign = -1;
  }
  else if(fabs(period-k1_alias_period)<=sloppy_local_tolerance){
    sprintf(str,"K1 Alias");
    period_info.period = k1_alias_period;
    period_info.omega = k1_alias_omega;
    period_info.tide = 0;//Alias periods are NOT tides!
  }
  else if(fabs(period-k2_alias_period)<=sloppy_local_tolerance){
    sprintf(str,"K2 Alias");
    period_info.period = k2_alias_period;
    period_info.omega = k2_alias_omega;
    period_info.tide = 0;//Alias periods are NOT tides!
  }
  else if(fabs(period-p1_alias_period)<=sloppy_local_tolerance){
    sprintf(str,"P1 Alias");
    period_info.period = p1_alias_period;
    period_info.omega = p1_alias_omega;
    period_info.tide = 0;//Alias periods are NOT tides!
  }
  else if(fabs(period-o1_alias_period)<=sloppy_local_tolerance){
    sprintf(str,"O1 Alias");
    period_info.period = o1_alias_period;
    period_info.omega = o1_alias_omega;
    period_info.tide = 0;//Alias periods are NOT tides!
  }
  else{
    //The one place I will allow periods to be measured in hours is in the
    //titles of the plots.
    sprintf(str,"period= %10.5f hours",period/3600.0);
    period_info.period = period;
    period_info.omega = twoPi/(period);
    period_info.recognized = 0;
    period_info.tide = 0;//If it's not recognized, assume it's not a tide.
  }

  if(period_info.tide == 1 and period_info.doodson.numbers.at(1) == 0 and period_info.doodson.numbers.at(2) == 0 and period_info.doodson.numbers.at(3) == 0 and period_info.doodson.numbers.at(4) == 0 and period_info.doodson.numbers.at(5) == 0 and period_info.doodson.numbers.at(6) == 0) cout<<"WARNING! Period "<<str<<" doesn't have associated Doodson numbers!"<<endl;

  period_info.title = str;
}

void convert_title_to_parameter(string title, period_info_s &period_info){
  /**********************************************************************
  Purpose: This function examines the title (presumably obtained from a
          previously generated output file) and tries to figure out what
          analysis parameter it refers to.  For instance, if the string
          is "M2 Amplitude" or "M2 Phase", it would recognize this as M2,
          call it a "tide", record the period in seconds and record the
          angular frequency (omega) as radians per second.
  ***********************************************************************
  Input:  string title - title of a previously generated output file.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //These need to be first because another period, M2, is a subset of both:
  if((int)title.find("M2 + 1 cycle/year")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos)
    is_this_period_recognized(m2_plus_period, period_info);

  else if((int)title.find("M2 - 1 cycle/year")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(m2_minus_period, period_info);
  else if((int)title.find("M2")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(m2_period, period_info);
  else if((int)title.find("M4")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(m4_period, period_info);
  else if((int)title.find("K1")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(k1_period, period_info);
  else if((int)title.find("S2")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(s2_period, period_info);
  else if((int)title.find("S1")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(s1_period, period_info);
  else if((int)title.find("O1")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(o1_period, period_info);
  else if((int)title.find("P1")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(p1_period, period_info);
  //Got to put 2N2 in front of N2 otherwise bad things happen!
  else if((int)title.find("2N2")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(two_n2_period, period_info);
  else if((int)title.find("N2")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(n2_period, period_info);
  else if((int)title.find("K2")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(k2_period, period_info);
  else if((int)title.find("Mf")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(mf_period, period_info);
  else if((int)title.find("Q1")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(q1_period, period_info);
  else if((int)title.find("Lunar Sidereal")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(lunar_sidereal_period, period_info);
  else if((int)title.find("Semi-Annual")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(semiannual_period, period_info);
  else if((int)title.find("Annual")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(annual_period, period_info);
  else if((int)title.find("4 Year")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(four_year_period, period_info);
  else if((int)title.find("Mm")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(mm_period, period_info);
  //Early FES routines used MSqm.
  else if(((int)title.find("Msqm")!=(int)string::npos or (int)title.find("MSqm")!=(int)string::npos) and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(msqm_period, period_info);
  else if((int)title.find("Mtm")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos) is_this_period_recognized(mtm_period, period_info);
  //New parameter types should be added here.
  else if((int)title.find("Constant")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos){
    period_info.title = "Constant";
    period_info.period = 0.0;
    period_info.tide = 0;
    period_info.parameter_type = 0;
    period_info.output_type = period_info.parameter_type;
  }//Old maps redundantly called the secular trend the "linear trend."
  else if(((int)title.find("Linear")!=(int)string::npos or (int)title.find("Trend")!=(int)string::npos) and (int)title.find("Alias")==(int)string::npos){
    period_info.title = "Trend";
    period_info.period = 0.0;
    period_info.tide = 0;
    period_info.parameter_type = 1;
    period_info.output_type = period_info.parameter_type;
  }
  else if((int)title.find("Acceleration")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos){
    period_info.title = "Acceleration";
    period_info.period = 0.0;
    period_info.tide = 0;
    period_info.parameter_type = 2;
    period_info.output_type = period_info.parameter_type;
  }
  else if((int)title.find("Time^")!=(int)string::npos and (int)title.find("Alias")==(int)string::npos){
    int power=-1;
    sscanf(title.c_str(),"Time^%d Term", &power);
    //cout<<"Load_output() sees title "<<title<<" and extracts power "<<power<<endl;
    period_info.title = title;//Why not? There's no "amplitude" or "sine coefficient" at end.
    period_info.period = 0.0;
    period_info.tide = 0;
    period_info.parameter_type = power;
    period_info.output_type = period_info.parameter_type;
  }
  else if((int)title.find("K1 Alias")!=(int)string::npos) is_this_period_recognized(k1_alias_period, period_info);
  else if((int)title.find("K2 Alias")!=(int)string::npos) is_this_period_recognized(k2_alias_period, period_info);
  else if((int)title.find("O1 Alias")!=(int)string::npos) is_this_period_recognized(o1_alias_period, period_info);
  else if((int)title.find("P1 Alias")!=(int)string::npos) is_this_period_recognized(p1_alias_period, period_info);
  else cout<<"WARNING! Couldn't recognize title "<<title<<endl;

  //If recognized as a sinusoid, is it an amp,phase,cos,sinamp?
  if(period_info.parameter_type == 101){
    if((int)title.find("Cosine")!=(int)string::npos){
      period_info.output_type = 101;
      period_info.sincos = 1;
    }
    else if((int)title.find("Sine")!=(int)string::npos){
      period_info.output_type = 102;
      period_info.sincos = 1;
    }
    else if((int)title.find("Amplitude")!=(int)string::npos){
      period_info.output_type = 103;
      period_info.sincos = 0;
    }
    else if((int)title.find("Phase")!=(int)string::npos){
      period_info.output_type = 104;
      period_info.sincos = 0;
    }
    else cout<<"!!!WARNING!!! Title"<<title<<" doesn't seem to be an amp,phase,cosamp or sinamp!!!"<<endl;
  }
}

void search_input_cache_for_binindices(input_s &input,
                                       string &filename,
                                       int &matching_cache_found){
  /**********************************************************************
  Purpose: This function searches the input cache folder (held in
          input_options) for the input cache file with filename as given
          in the arguments. If a file is found, the function sets
          matching_cache_found to 1, otherwise it's 0.  This way, you can
          decide outside of this program whether or not to use the input
          that is returned either way. (input is empty if no file is found.)
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,l,j;
  char s[max_length];
  string basefolder(gaiafolder());
  string file;
  long long utemp;//Temporarily holds input.binindices values.
  //Region code uses a input cache file, which is also dependent on
  //the grid options.
  DIR *cacheDIR;
  struct dirent *cacheDirEnt;
  //These variables are used to load a matching input from cache.
  FILE *in_fp;
  //These variables are used to store a new cache file.
  string newfile = input.options.cachefolder;

  char* f_return;//Used to stop fgets return value warning.

  //Check to see if the cache contains a file with the same options
  //as the ones loaded in input/grid.options.  If so, set i=1.
  i=0;//Init as 0 because it hasn't found matching input in cache yet.

  //Open the cache folder.
  cacheDIR = opendir(input.options.cachefolder.c_str());
  //Examine each file, see if it contains the desired input.
  cacheDirEnt = readdir(cacheDIR);
  while(cacheDirEnt != NULL && i == 0){
    //If this filename matches desired input options, set i=1 to exit loop.
    if(strcmp(filename.c_str(),cacheDirEnt->d_name)==0) i = 1;
    //printf("%s",cacheDirEnt->d_name); cout<<endl;
    cacheDirEnt = readdir(cacheDIR);
  }

  // Release the open folder.
  closedir(cacheDIR);

  //If matching file was found, load it and skip rest of function.
  if(i == 1){
    cout<<"Loading input.binindices from cache..."<<endl;
    //Prepend directory to filename, then open.
    newfile.append(filename);
    in_fp = fopen(newfile.c_str(),"r");
    //Load the first header line which contains number of points.
    f_return=fgets(s,max_length,in_fp);
    sscanf(s,"%lld", &l);
    //Use # of points to resize vector.
    input.binindices.resize(l);
    //Load second line which contains input.epoch.
    f_return=fgets(s,max_length,in_fp);
    sscanf(s,"%lld", &input.epoch);
    //Load third line which contains input's first time.
    f_return=fgets(s,max_length,in_fp);
    sscanf(s,"%lld", &input.first_time);
    //Load fourth line which contains input's last time.
    f_return=fgets(s,max_length,in_fp);
    sscanf(s,"%lld", &input.last_time);
    //Read in input bin indices.
    for(j=0;j<(long long)input.binindices.size();j++){
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &utemp);
      input.binindices.at(j) = utemp;
    }
    fclose(in_fp);
    printf("Finished. %lld input points binned from cache.",(long long)input.binindices.size());
    cout<<endl;

    //Even if input_split==0, this is still needed.
    input.verification_points.clear();
    input.verification_points.resize(input.binindices.size(),0);

    //debug: binindices code didn't work correctly at first, so I forced
    //even region code to load a complete copy of the input file.
    //if(region.lat.size()>0) goto binindices_debug1;
  }
  matching_cache_found = i;
}

void swap_double(double *i, double *j){
  /**********************************************************************
  Purpose: This function swaps the values in its two (double) arguments.
  ***********************************************************************
  Input:
            double i,j - Two doubles to be swapped
  ***********************************************************************
  Output:
            double i,j - The doubles are sent via reference.
  **********************************************************************/
  double t = *i;
  *i = *j;
  *j = t;
}

void swap_long(long long *i, long long *j){
  /**********************************************************************
  Purpose: This function swaps the values in its two (long long) args.
  ***********************************************************************
  Input:
            double i,j - Two long longs to be swapped
  ***********************************************************************
  Output:
            double i,j - The long longs are sent via reference.
  **********************************************************************/
  long long t = *i;
  *i = *j;
  *j = t;
}

void init_time_conversion(time_conversion_s &time_conversion){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;
  //Use != 1 because this variable might not be preset (ironic...)
  if(time_conversion.initialized != 1){
    //Controls how the initialization routines calculate seconds/days per
    //year.  Should be the max/min years I expect to encounter!
    long long start_year = 1000, end_year = 3000;
    vector<long long>::iterator iyear;//Used to search time_conversion.year vector.

    //define for non-leap years and only modify feb (index 2) each time.
    time_conversion.dpm.resize(13,31);
    time_conversion.dpm.at(2) = 28;
    time_conversion.dpm.at(4) = 30;
    time_conversion.dpm.at(6) = 30;
    time_conversion.dpm.at(9) = 30;
    time_conversion.dpm.at(11) = 30;

    for(i=start_year;i<=end_year;i++){
      time_conversion.year.push_back(i);
      //Modify dpm.at(2) (feb) based on whether "i" is a leap year in the
      //Gregorian calendar.  Pseudocode provided by wikipedia.
      if((i % 4 == 0 and i % 100 != 0) or i % 400 == 0) time_conversion.dpm.at(2) = 29;
      else time_conversion.dpm.at(2) = 28;

      //Calculate and save days in this year.
      long long days_in_year = 0;
      for(j=1;j<=12;j++) days_in_year += time_conversion.dpm.at(j);
      time_conversion.jul_days_in_year.push_back(days_in_year);

      //Calculate and save seconds in this year.
      time_conversion.utc_seconds_in_year.push_back(days_in_year*86400);
    }

    //Check to make sure that 1999 is in the t_c.year vector.
    iyear = find(time_conversion.year.begin(),time_conversion.year.end(),1999);
    if(iyear != time_conversion.year.end()) time_conversion.pos1999 = (long long)(iyear - time_conversion.year.begin());
    else{
      cout << "WARNING!! Year "<<1999<<" was not found in t_c.year!"<<endl;
      time_conversion.pos1999 = -1;
    }

    //Check to make sure that 2000 is in the t_c.year vector.
    iyear = find(time_conversion.year.begin(),time_conversion.year.end(),2000);
    if(iyear != time_conversion.year.end()) time_conversion.pos2000 = (long long)(iyear - time_conversion.year.begin());
    else{
      cout << "WARNING!! Year "<<2000<<" was not found in t_c.year!"<<endl;
      time_conversion.pos2000 = -1;
    }
    time_conversion.initialized = 1;
  }
  
  //define for non-leap years and only modify feb (index 2) each time.
  time_conversion.dpm.resize(13,31);
  time_conversion.dpm.at(2) = 28;
  time_conversion.dpm.at(4) = 30;
  time_conversion.dpm.at(6) = 30;
  time_conversion.dpm.at(9) = 30;
  time_conversion.dpm.at(11) = 30;
  //Only run this code if calendar_time has more than 0 elements.
  //Otherwise it will crash, and it doesn't really matter in that case anyway.
  if(time_conversion.calendar_time.size() > 0){
    //Modify dpm.at(2) (feb) based on whether "i" is a leap year in the
    //Gregorian calendar.  Pseudocode provided by wikipedia.
    i = time_conversion.calendar_time.front();
    if((i % 4 == 0 and i % 100 != 0) or i % 400 == 0) time_conversion.dpm.at(2) = 29;
    else time_conversion.dpm.at(2) = 28;
  }
}

void cal2utc(time_conversion_s &time_conversion){
  /**********************************************************************
  Purpose: This function converts a calendar date and time to UTC seconds
          past J2000.

          The Gregorian calendar is assumed, and date/time is GMT.

          WARNING!  On 32-bit machines, long integers can only hold
          2147483647 as their largest value.  This means that any calendar
          date prior to approximately January 1932 can NOT be represented
          in terms of seconds past J2000, because it will overflow and be
          represented by -2147483648 (but this error mode will depend on
          the compiler, most likely...).
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  vector<long long>::iterator iyear;//Used to search time_conversion.year vector.
  long long pos;//Contains iyear converted to integer index to access t_c.secs_yr.

  //Clear the return value in case it held another value previously.
  time_conversion.utc_seconds = 0;

  init_time_conversion(time_conversion);

  //Count up to Jan 1 at 00:00:00 of the next year, then use the
  //vectors t_c.utc_seconds_in_year, t_c.year to finish the calculation.
  //First, calculate number of seconds from the specified second to the end
  //of the current year.  Do this in stages.
  //Note that I'm actually subtracting the seconds to the next year.  This
  //is because if the given year is 1999 or earlier, the utc_seconds value
  //should be negative anyway.  If the year is 2000 or later, the value
  //held in utc_seconds will be added to the number of seconds from the
  //"next year" to the J2000 epoch.

  //Stage 1 - number of seconds left until the next minute.
  time_conversion.utc_seconds -=(60-time_conversion.calendar_time.at(5));
  //Stage 2 - from that point, # minutes left until next hour, times 60.
  time_conversion.utc_seconds -=(59-time_conversion.calendar_time.at(4))*60;
  //Stage 3 - from that point, hours until midnight, times 3600.
  time_conversion.utc_seconds -=(23-time_conversion.calendar_time.at(3))*3600;
  //Stage 4 - from that point, number of days left in month, times 3600*24.
  //cout<<"time_conversion.dpm.at("<<time_conversion.calendar_time.at(1)<<") = "<<time_conversion.dpm.at(time_conversion.calendar_time.at(1))<<endl;
  time_conversion.utc_seconds -=(time_conversion.dpm.at(time_conversion.calendar_time.at(1))-time_conversion.calendar_time.at(2))*86400;
  //Stage 5 - from that point, number of days left in year, times 3600*24.
  for(i=time_conversion.calendar_time.at(1)+1;i<=12;i++){
    time_conversion.utc_seconds -= time_conversion.dpm.at(i)*86400;
    //cout<<"time_conversion.dpm.at("<<i<<") = "<<time_conversion.dpm.at(i)<<endl;
  }
  //cout<<"before if statement: "<<time_conversion.utc_seconds<<endl;
  //Now treat years 1999 and earlier differently than years 2000 and later.
  if(time_conversion.calendar_time.front() == 1999);//Do nothing.
  else if(time_conversion.calendar_time.front() < 1999){
    //Now add seconds per year from "specified year + 1" to 1999 (inclusive)
    //Do this by finding each of these entries in the t_c.year vector, then
    //convert those iterators to long indices to sum up the values in b/w
    //the two entries (inclusive) in the t_c.utc_seconds_in_year vector.

    //Check to make sure that "specified yr+1" is in the t_c.year vector.
    iyear = find(time_conversion.year.begin(),time_conversion.year.end(),time_conversion.calendar_time.front()+1);
    if(iyear != time_conversion.year.end()) pos = (long long)(iyear - time_conversion.year.begin());
    else{
      cout<<"WARNING!! Year "<<time_conversion.calendar_time.front()+1<<" was not found in t_c.year!"<<endl;
      pos = -1;
    }
    for(i=pos;i<=time_conversion.pos1999;i++) time_conversion.utc_seconds -= time_conversion.utc_seconds_in_year.at(i);
  }
  else if(time_conversion.calendar_time.front() >= 2000){
    //Now add seconds per year from the year 2000 to "specified year".
    //Do this by finding each of these entries in the t_c.year vector, then
    //convert those iterators to long indices to sum up the values in b/w
    //the two entries (inclusive) in the t_c.utc_seconds_in_year vector.
    //Note that I'm adding seconds up to the specified year (inclusive)
    //because I just SUBTRACTED the seconds from the specified date to the
    //end of the current year.  For times after 2000-01-01, this means that
    //I need to add the seconds in all the years from 2000 to specified year

    //Check to make sure that "specified yr" is in the t_c.year vector.
    iyear = find(time_conversion.year.begin(),time_conversion.year.end(),time_conversion.calendar_time.front());
    if(iyear != time_conversion.year.end()) pos = (long long)(iyear - time_conversion.year.begin());
    else{
      cout << "WARNING!! Year "<<time_conversion.calendar_time.front()<<" was not found in t_c.year!"<<endl;
      pos = -1;
      cout<<"t_c.year.size() = "<<time_conversion.year.size()<<endl;
    }

    //cout<<"in if statement: pos2000, pos = "<<time_conversion.pos2000<<", "<<pos<<endl;
    for(i=time_conversion.pos2000;i<=pos;i++){
      time_conversion.utc_seconds += time_conversion.utc_seconds_in_year.at(i);
      //cout<<"sec in year: "<<time_conversion.utc_seconds_in_year.at(i)<<endl;
    }
    //cout<<"before subtraction of 12 hours: "<<time_conversion.utc_seconds<<endl;
  }
  
  //Subtract 12 hours b/c J2000 is at noon on Jan 1, not midnight!
  time_conversion.utc_seconds -= 43200;
}

void dyr2utc(time_conversion_s &time_conversion){
  /**********************************************************************
  Purpose: This function converts a decimal year (e.g. 2000.17) to
          UTC seconds past J2000.

          The Gregorian calendar is assumed, and date/time is GMT.

          WARNING!  On 32-bit machines, long integers can only hold
          2147483647 as their largest value.  This means that any calendar
          date prior to approximately January 1932 can NOT be represented
          in terms of seconds past J2000, because it will overflow and be
          represented by -2147483648 (but this error mode will depend on
          the compiler, most likely...).
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;

  //Clear the return value in case it held another value previously.
  time_conversion.utc_seconds = 0;

  time_conversion.calendar_time.resize(6,0);
  time_conversion.calendar_time.front() = (long long)floor(time_conversion.dyr);
  time_conversion.calendar_time.at(1) = 1;//month - 0 is meaningless!
  time_conversion.calendar_time.at(2) = 1;//day - 0 is meaningless!
  time_conversion.calendar_time.at(3) = 0;//hours
  time_conversion.calendar_time.at(4) = 0;//minutes
  time_conversion.calendar_time.at(5) = 0;//seconds
  cal2utc(time_conversion);//Convert calendar time to UTC seconds.
  
  i = (long long)((double)seconds_in_year*(time_conversion.dyr - (double)floor(time_conversion.dyr)));
  time_conversion.utc_seconds += i;
}

void utc2dyr(time_conversion_s &time_conversion){
  /**********************************************************************
  Purpose: This function converts UTC seconds past J2000 to decimal year
          (e.g. 2000.17).

          The Gregorian calendar is assumed, and date/time is GMT.

          WARNING!  On 32-bit machines, long integers can only hold
          2147483647 as their largest value.  This means that any calendar
          date prior to approximately January 1932 can NOT be represented
          in terms of seconds past J2000, because it will overflow and be
          represented by -2147483648 (but this error mode will depend on
          the compiler, most likely...).
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i = time_conversion.utc_seconds;

  //Clear the return value in case it held another value previously.
  time_conversion.dyr = 0;

  utc2cal(time_conversion);//Convert UTC seconds to calendar time.
  time_conversion.calendar_time.at(1) = 1;//month - 0 is meaningless!
  time_conversion.calendar_time.at(2) = 1;//day - 0 is meaningless!
  time_conversion.calendar_time.at(3) = 0;//hours
  time_conversion.calendar_time.at(4) = 0;//minutes
  time_conversion.calendar_time.at(5) = 0;//seconds
  cal2utc(time_conversion);//Convert calendar time to UTC seconds.

  time_conversion.dyr = (double)time_conversion.calendar_time.at(0) + (double)(i - time_conversion.utc_seconds)/(double)seconds_in_year;
  time_conversion.utc_seconds = i;//Restore original value.
}

void utc2cal(time_conversion_s &time_conversion){
  /**********************************************************************
  Purpose: This function converts time in J2000 seconds to a
           vector of integers representing the calendar date.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Rawtime is in UNIX time, rel to midnight Jan 1, 1970, hence the sum.
  time_t rawtime = time_conversion.utc_seconds+946728000;
  tm * ptm;
  ptm = gmtime(&rawtime);
  time_conversion.calendar_time.resize(6,0);
  time_conversion.calendar_time.front() = ptm->tm_year+1900;//year
  time_conversion.calendar_time.at(1) = ptm->tm_mon;//month - 0 is meaningless!
  time_conversion.calendar_time.at(2) = ptm->tm_mday;//day - 0 is meaningless!
  time_conversion.calendar_time.at(3) = ptm->tm_hour;//hours
  time_conversion.calendar_time.at(4) = ptm->tm_min;//minutes
  time_conversion.calendar_time.at(5) = ptm->tm_sec;//seconds
}

void utc2fes_jul(time_conversion_s &time_conversion){
  /**********************************************************************
  Purpose: This function converts UTC seconds since J2000 to Julian days
          since jan-1-1950-00:00.00, which is how FES defines Julian dates.

          WARNING!  2009-08-20 - John Wahr noticed that FES internally
                                 converts 1950-1-1.0 to 1900-1-1.0.
                                 So FES Julian days may be relative to
                                 1900. As a result, this function was
                                 changed to subtract fes_utc_epoch
                                 rather than using the number of seconds
                                 from 1950-1-1.0 to J2000 as before.

          The calendar date/time is assumed to be in GMT.

          WARNING!  On 32-bit machines, long integers can only hold
          2147483647 as their largest value.  This means that any calendar
          date prior to approximately January 1932 can NOT be represented
          in terms of seconds past J2000, because it will overflow and be
          represented by -2147483648 (but this error mode will depend on
          the compiler, most likely...).
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Clear the return value in case it held another value previously.
  time_conversion.fes_jul_days = 0;

  init_time_conversion(time_conversion);

  //First convert UTC since J2000 to UTC seconds since the FES epoch,
  //whether that's 1950-1-1.0 or 1900-1-1.0. fes_utc_epoch- defined in
  //definitions.hpp- will determine which of these is true.
  //Then convert from UTC seconds to Julian days since the FES epoch
  //by dividing by 86400.
  time_conversion.fes_jul_days=(double)(time_conversion.utc_seconds - fes_utc_ext_epoch)/86400.0;
}

void jd2utc(time_conversion_s &time_conversion){
  /**********************************************************************
  Purpose: This function converts a standard Julian date to UTC seconds
           since J2000.

          WARNING!  On 32-bit machines, long integers can only hold
          2147483647 as their largest value.  This means that any calendar
          date prior to approximately January 1932 can NOT be represented
          in terms of seconds past J2000, because it will overflow and be
          represented by -2147483648 (but this error mode will depend on
          the compiler, most likely...).
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Clear the return value in case it held another value previously.
  time_conversion.utc_seconds = 0;

  init_time_conversion(time_conversion);

  //First subtract the standard Julian date of J2000, then multiply by
  //86400 to convert from days to seconds.
  time_conversion.utc_seconds=(long)((time_conversion.jd - 2451545.0)*86400.0);
}

void cal2fes_jul(time_conversion_s &time_conversion){
  /**********************************************************************
  Purpose: This function converts a calendar date and time to Julian days
          since jan-1-1950-00:00.00, which is how FES defines Julian dates.

          The Gregorian calendar is assumed, and date/time is GMT.

          WARNING!  On 32-bit machines, long integers can only hold
          2147483647 as their largest value.  This means that any calendar
          date prior to approximately January 1932 can NOT be represented
          in terms of seconds past J2000, because it will overflow and be
          represented by -2147483648 (but this error mode will depend on
          the compiler, most likely...).
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //No need to clear the return value or initialize because this function
  //will call two functions that will do that regardless.
  cal2utc(time_conversion);
  utc2fes_jul(time_conversion);
}

string sec2human(timespan_conversion_s &timespan_conversion){
  /**********************************************************************
  Purpose: This function converts a timespan in seconds to a written format
          with years, weeks, days, hours, minutes, seconds.  Years are
          defined as 365.25 days, or 31,557,600 seconds.
          It's easiest to write this function with a space at the front,
          so that's why it's called without a space in front.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  string output;
  char time_statement[max_length];
  int i;
  int already_printed=0;//= number of units I've already printed to string.

  if(timespan_conversion.minute != 60){
    //So that I don't have to perform these calculations each time.
    timespan_conversion.minute = 60;
    timespan_conversion.hour = 3600;
    timespan_conversion.day = 86400;
    timespan_conversion.week = 604800;
    timespan_conversion.year = (long long)seconds_in_year;

    timespan_conversion.unit_titles.resize(6);
    timespan_conversion.unit_titles.front()="year";
    timespan_conversion.unit_titles.at(1)="week";
    timespan_conversion.unit_titles.at(2)="day";
    timespan_conversion.unit_titles.at(3)="hour";
    timespan_conversion.unit_titles.at(4)="minute";
    timespan_conversion.unit_titles.at(5)="second";
  }

  //In addition to string output, this function produces a vector that
  //contains the information in long form.
  timespan_conversion.ywdhms.resize(6,0);

  output = " ";//Need to initialize the output string so I can append to it.
  if(timespan_conversion.seconds < 0){
    output.append("-");
    timespan_conversion.negative = 1;
    timespan_conversion.seconds *= -1;//To make it positive without abs().
  }
  else timespan_conversion.negative = 0;

  //Calculate years, weeks, days, hours, minutes, seconds.
  timespan_conversion.ywdhms.front() = (long long)((double)timespan_conversion.seconds/31557600.0);//years
  timespan_conversion.ywdhms.at(1) = (long long)((double)(timespan_conversion.seconds - timespan_conversion.ywdhms.front()*timespan_conversion.year)/604800.0);//weeks
  timespan_conversion.ywdhms.at(2) = (long long)((double)(timespan_conversion.seconds - timespan_conversion.ywdhms.at(1)*timespan_conversion.week - timespan_conversion.ywdhms.front()*timespan_conversion.year)/86400.0);//days
  timespan_conversion.ywdhms.at(3) = (long long)((double)(timespan_conversion.seconds - timespan_conversion.ywdhms.at(2)*timespan_conversion.day - timespan_conversion.ywdhms.at(1)*timespan_conversion.week - timespan_conversion.ywdhms.front()*timespan_conversion.year)/3600.0);//hours
  timespan_conversion.ywdhms.at(4) = (long long)((double)(timespan_conversion.seconds - timespan_conversion.ywdhms.at(3)*timespan_conversion.hour - timespan_conversion.ywdhms.at(2)*timespan_conversion.day - timespan_conversion.ywdhms.at(1)*timespan_conversion.week - timespan_conversion.ywdhms.front()*timespan_conversion.year)/60.0);//minutes
  timespan_conversion.ywdhms.at(5) = timespan_conversion.seconds - timespan_conversion.ywdhms.at(4)*timespan_conversion.minute - timespan_conversion.ywdhms.at(3)*timespan_conversion.hour - timespan_conversion.ywdhms.at(2)*timespan_conversion.day - timespan_conversion.ywdhms.at(1)*timespan_conversion.week - timespan_conversion.ywdhms.front()*timespan_conversion.year;//seconds

  //Loop through all units of time, append them if not zero and
  //less than "maxunits" units have been printed already for brevity.
  int maxunits=3;
  for(i=0;i<6;i++){
    if(timespan_conversion.ywdhms.at(i) > 0 and already_printed < maxunits){
      if(already_printed > 0) output.append(", ");
      sprintf(time_statement,"%2lld",timespan_conversion.ywdhms.at(i));
      output.append(time_statement); output.append(" ");
      output.append(timespan_conversion.unit_titles.at(i));
      if(timespan_conversion.ywdhms.at(i) > 1) output.append("s");
      already_printed++;
    }
  }
  if(already_printed==0) output.append("0 seconds");

  return output;
}

string seconds_to_string(long long i){
  /**********************************************************************
  Purpose: This function converts a timespan in seconds to a written format
          with years, weeks, days, hours, minutes, seconds.
          seconds_in_years is defined as 365.2422 days via a static
          constant in a .hpp file.

          This version is briefer than sec2human() but returns less output.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  string output;

  timespan_conversion_s timespan_conversion;

  timespan_conversion.seconds=i;
  output = sec2human(timespan_conversion);

  return output;
}

string day2human(timespan_conversion_s &timespan_conversion){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  string temp;
  timespan_conversion.seconds = (long long)(timespan_conversion.days*86400.0);
  temp = sec2human(timespan_conversion);
  return temp;
}

string current_datetime(){
  /**********************************************************************
  Purpose: This function returns a string with the current date+time
          in a concise prefix fashion: "YYYYMMDD-HHMMSS-"
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          string filename - Contains the filename (no path!).
  **********************************************************************/
  //Used to create foldername.
  char str[max_length];//Temp storage.
  time_t start_time;
  struct tm * timeinfo;
  string prefix;

  time(&start_time);//Record time at start of this function.
  timeinfo = localtime(&start_time);
  strftime(str,80,"%Y%m%d-%H%M%S",timeinfo);
  prefix = str;
  return prefix;
}

void ksum(kahan_s &kahan){
  /**********************************************************************
  Purpose: This function sums whatever numbers are sent to it using Kahan
          summation, which corrects for loss of precision when adding
          a small number to a big number (or, equivalently, adding up LOTS
          of numbers).
          The sole argument is a structure called kahan.  It contains:

          sum -double- that holds the final sum
          c - double - compensation for lost low-order bits- running tally.
          x - double - current number to be added.
          init - int - 1 if kahan struct needs to be (re-)initialized.

          It should be used like this:

          kahan_s kahan; kahan.init = 1;//initialize sum and c.
          for(y=many numbers to sum together){ kahan.x = y; ksum(kahan); }
          end_result = kahan.sum;
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  double t;//temp

  //First call with init==1 sets sum and c to 0, then sets init to 0.
  if(kahan.init == 1){
    kahan.sum = 0.0;
    kahan.c = 0.0;
    kahan.init=0;
  }

  //Use running tally of lost digits to nudge the current number - kahan.x.
  kahan.x -= kahan.c;//These numbers should be similar.

  //sum is large and x is small, so many digits of x are lost in addition.
  t = kahan.sum + kahan.x;

  //1st - (t-kahan.sum) gives effective (large) digits of x.
  //2nd - Subtracting the actual (well, compensated) value of x gives
  //      the negative of the digits that didn't make it.  Save them and
  //      subtract them from the next x- they aren't being lost.
  kahan.c = (t - kahan.sum) - kahan.x;

  kahan.sum = t;
}

void ksum_vector(kahan_s &kvector){
  /**********************************************************************
  Purpose: This function is sent multiple gsl vectors, and sums them using
          Kahan summation, which corrects for loss of precision when adding
          a small number to a big number (or, equivalently, adding up LOTS
          of numbers).
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE
  //First call with init==1 allocs and zeros vectors.
  if(kvector.init == 1){
    //Allocate the same num of elements to kvector.sum as are in kvector.x
    kvector.sum_vector = gsl_vector_alloc(kvector.size);
    //Set all those elements to zero.
    gsl_vector_set_zero(kvector.sum_vector);

    //Do the same thing for the compensation vector.
    kvector.c_vector = gsl_vector_alloc(kvector.size);
    gsl_vector_set_zero(kvector.c_vector);

    //Init temp vector.
    kvector.temp_vector1 = gsl_vector_alloc(kvector.size);

    //Note that kvector has been initialized.
    kvector.init=0;
  }

  //debug.
  //cout<<"max element of c_vector: "<<gsl_vector_max(kvector.c_vector)<<endl;
  //cout<<"max element of x_vector: "<<gsl_vector_max(kvector.x_vector)<<endl;
  //cout<<"max element of sum_vector: "<<gsl_vector_max(kvector.sum_vector)<<endl;

  //Use running tally of lost digits to nudge the current vector.
  gsl_vector_sub(kvector.x_vector,kvector.c_vector);

  //Calculate t = sum + x.
  gsl_vector_memcpy(kvector.temp_vector1,kvector.x_vector);
  gsl_vector_add(kvector.temp_vector1,kvector.sum_vector);

  //Calculate the "remainder"- lost digits that will be added to x_vector
  //next time.
  //This set of operations calculates:
  //kvector.c_vector = (kvector.temp_vector1 - kvector.sum_vector) - kvector.x_vector;
  gsl_vector_sub(kvector.temp_vector1,kvector.sum_vector);
  gsl_vector_sub(kvector.temp_vector1,kvector.x_vector);
  gsl_vector_memcpy(kvector.c_vector,kvector.temp_vector1);

  //Finally finished - record sum in
  gsl_vector_add(kvector.sum_vector,kvector.x_vector);
  #endif
}

void ksum_matrix(kahan_s &kmatrix){
  /**********************************************************************
  Purpose: This function is sent multiple gsl matrices, and sums them using
          Kahan summation, which corrects for loss of precision when adding
          a small number to a big number (or, equivalently, adding up LOTS
          of numbers).
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE
  //First call with init==1 allocs and zeros matrixs.
  if(kmatrix.init == 1){
    //Allocate the same num of elements to kmatrix.sum as are in kmatrix.x
    kmatrix.sum_matrix = gsl_matrix_alloc(kmatrix.size,kmatrix.size);
    //Set all those elements to zero.
    gsl_matrix_set_zero(kmatrix.sum_matrix);

    //Do the same thing for the compensation matrix.
    kmatrix.c_matrix = gsl_matrix_alloc(kmatrix.size,kmatrix.size);
    gsl_matrix_set_zero(kmatrix.c_matrix);

    //Init temp matrix.
    kmatrix.temp_matrix1 = gsl_matrix_alloc(kmatrix.size,kmatrix.size);

    //Note that kmatrix has been initialized.
    kmatrix.init=0;
  }

  //Use running tally of lost digits to nudge the current matrix.
  gsl_matrix_sub(kmatrix.x_matrix,kmatrix.c_matrix);

  //Calculate t = sum + x.
  gsl_matrix_memcpy(kmatrix.temp_matrix1,kmatrix.x_matrix);
  gsl_matrix_add(kmatrix.temp_matrix1,kmatrix.sum_matrix);

  //Calculate the "remainder"- lost digits that will be added to x_matrix
  //next time.
  //This set of operations calculates:
  //kmatrix.c_matrix = (kmatrix.temp_matrix1 - kmatrix.sum_matrix) - kmatrix.x_matrix;
  gsl_matrix_sub(kmatrix.temp_matrix1,kmatrix.sum_matrix);
  gsl_matrix_sub(kmatrix.temp_matrix1,kmatrix.x_matrix);
  gsl_matrix_memcpy(kmatrix.c_matrix,kmatrix.temp_matrix1);

  //Finally finished - increment sum in sum_matrix
  gsl_matrix_add(kmatrix.sum_matrix,kmatrix.x_matrix);
  #endif
}

void cross(cross_product_s &cross_product){
  /**********************************************************************
  Purpose: This function calculates the cross product of A X B, where a and
          b are stored in the cross_product struct.  The result, c, is also
          stored in the cross_product struct.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //I checked the results of this cross product function on
  //2008-05-15 against an online calculator.  Results:
  /* You have entered the following vectors.
  * v1 = ( -202.191 , 93.9779 , 3.5837 )
  * v2 = ( 17.2228 , -136.393 , 6863.64 )
  Result of the requested computation.
  * Vector product: w = v1 wedge v2 = (645519.2651501,1387827.95658836,25958.87448688)

  This agrees with the output from function create_least_squares():
  !!WARNING!! SEP VECTOR IS BEING REPLACED WITH PERPENDICULAR VECTOR!!!!
  sep vector: <-202.191, 93.9779, 3.5837
  vertical vector: <17.2228, -136.393, 6863.64
  new sep vector: <645519, 1.38783e+06, 25958.8 */
  cross_product.c.resize(3);
  cross_product.c.front() = cross_product.a.at(1)*cross_product.b.at(2) - cross_product.a.at(2)*cross_product.b.at(1);
  cross_product.c.at(1) = cross_product.a.at(2)*cross_product.b.front() - cross_product.a.front()*cross_product.b.at(2);
  cross_product.c.at(2) = cross_product.a.front()*cross_product.b.at(1) - cross_product.a.at(1)*cross_product.b.front();
}

input_s load_another_input_segment(input_options_s &input_options,
                                   long long &mainindex){
  /**********************************************************************
  Purpose: This function loads a new segment of input data consisting of
          "input_options.segmentsize" points. The input long "mainindex"
          controls how the new segment is read. If this index is <0, no
          new input data is read (because the input file has been
          completely read) and input.time.size() is set to 0.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)

          long mainindex - Index at which to start reading the main input
                           file.  If this index<0, no new input data is
                           read and input.time.size() is set to 0.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  input_s input;
  double onelat,onelon,oneaccel;
  long long onetime,j,k;
  int oneascending;
  char s[max_length];
  string basefolder(gaiafolder());
  string file;

  char* f_return;//Used to stop fgets return value warning.

  //Increment mainindex at end, because there may not have been enough
  //points left in the input file!  Record whatever was left in numpoints.
  //Use numpoints to control the outer loop?  Is that... legal?
  //if mainindex < 0, don't bother loading.  Return a special code to break
  //out of the loop this function is called from?

  input.options = input_options;
  input.bad_indices.clear();//New segment, new pts that might need to be deleted.

  //Load more points from input data file if mainindex>=0
  if(mainindex >=0){
    basefolder.append(input.options.basefolder);
    file = basefolder;
    file.append(input.options.filename);

    //Open input file for loading.
    FILE *in_fp;
    in_fp = fopen(file.c_str(),"r");

    //Warn if file doesn't open correctly.
    if(!in_fp) cout << "The input data file, " << file
                     << ", failed to open."<<endl;
    else cout << "Loading "<<input.options.segmentsize<<" points from "
              << input.options.filename << " at index "<<mainindex<<"..";

    //Load the first header line separately because it contains no data.
    f_return=fgets(s,max_length,in_fp);

    //Skip to the next segment index using mainindex.
    if(mainindex > 0) for(j=0;j<mainindex;j++) f_return=fgets(s,max_length,in_fp);

    //"k" stores the current number of input points read, resets at input.options.segmentsize
    k = 0;
    while(fgets(s,max_length,in_fp)!=NULL and k<input.options.segmentsize){
      sscanf(s,ACCEL_READ_FORMAT, &onetime, &onelat, &onelon,
             &oneaccel, &oneascending);
      input.time.push_back(onetime);
      input.lat.push_back(onelat);
      input.lon.push_back(onelon);
      input.accel.push_back(oneaccel);
      input.ascending.push_back(oneascending);
      k++;//Another input point read.
    }//End of while loop.
    //If this is the end of the input file, set mainindex to -1.  Otherwise,
    //mainindex essentially tells this function how many input points to skip
    //when loading next.
    if((long long)input.time.size() == input.options.segmentsize) mainindex+=input.time.size();
    else mainindex = -1;
    cout << ". loaded "<<input.time.size()<<" data points."<<endl;
    fclose(in_fp);
  }//End of "if mainindex>=0".
  return input;
}

void append_input_and_orbit_data(input_s &input,
                                 long long &mainindex){
  /**********************************************************************
  Purpose: This function saves the current input and orbit data to the end
          of the input_and_orbit.txt file.  It then clears the input
          structure completely.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  string basefolder(gaiafolder());
  string file;

  //Ascii AND (eventually) binary!
  cout<<"Finished this segment of "<<input.time.size()<<" input points.  Writing to disk..."<<input.options.output_filename<<endl;
  basefolder.append(input.options.basefolder);
  file = basefolder;
  file.append(input.options.output_filename);

  //Open output file for appending (or writing if this is the first loop!).
  FILE *in_fp;//technically an "out_fp" but who cares?
  //The first loop has mainindex equal to the value it was assigned by the
  //first run of load_another_input_segment, which is probably(!)
  //going to be segmentsize!
  if(mainindex==input.options.segmentsize) in_fp = fopen(file.c_str(),"w");
  else in_fp = fopen(file.c_str(),"a");
  if(mainindex==input.options.segmentsize) fprintf(in_fp,ORBIT_HEADER);
  for(i=0;i<(long long)input.time.size();i++){
    //Don't record pts that were marked as bad.
    if(count(input.bad_indices.begin(),input.bad_indices.end(),i) == 0) fprintf(in_fp,ORBIT_WRITE_FORMAT,input.time.at(i), input.lat.at(i), input.lon.at(i), input.accel.at(i), input.ascending.at(i),input.xA.at(i),input.xB.at(i),input.yA.at(i),input.yB.at(i),input.zA.at(i),input.zB.at(i));
  }
  cout<<"Finished writing new input_and_orbit file."<<endl;
  fclose(in_fp);
  clear_input(input);
}

void clear_input(input_s &input){
  /**********************************************************************
  Purpose: This fctn clears all input data.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  input.time.clear();
  input.lat.clear();
  input.lon.clear();
  input.ascending.clear();
  input.accel.clear();
  input.xA.clear();
  input.xB.clear();
  input.yA.clear();
  input.yB.clear();
  input.zA.clear();
  input.zB.clear();
}

void load_output(results_s &results,
                 plot_options_s &plot_options,
                 grid_s &grid){
  /**********************************************************************
  Purpose: This fctn loads output files and reconstructs parameter arrays
          that were used to create output files.  It's different from
          read_output because this reads output_filenames.txt
          to figure out how many output files exist, then it uses
          read_output to load each individual file.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k,l;
  char s[max_length];
  period_info_s period_info;
  input_s temp_input;
  results_s temp_results;
  vector <string> recognized_titles;
  string filename(gaiafolder());
  int npar;
  FILE *new_fp;
  string new_file(gaiafolder()),other_file;
  int encountered_period;//Used to set sincos correctly.
  string folder_to_load(gaiafolder());
  folder_to_load.append(plot_options.inputfolder);

  char* f_return;//Used to stop fgets return value warning.

  cout<<"Loading output files and replacing parameter arrays (and grid, etc) with values reconstructed from the output files."<<endl;

  //parameter/type vectors are usually filled with irrelevant values.
  results.options.parameter.clear();
  results.options.parameter_type.clear();
  results.options.parameter_tide.clear();

  //Clear these vectors in case this function is called more than once.
  results.options.output_type.clear();
  results.options.output_tide.clear();

  //Look for output_filenames.txt.
  new_file = folder_to_load;
  new_file.append("output_filenames.txt");

  //Loading files from folder specified in folder_to_load.
  //This variable will be the base of the filenames in the master file.
  filename = folder_to_load;

  //Open and read # o files from folder_to_load/output_filenames.txt.
  new_fp = fopen(new_file.c_str(),"r");
  if(new_fp == NULL) cout<<"!!WARNING!! output_filenames.txt wasn't found in folder "<<folder_to_load<<endl;

  //Ignore the header line.
  f_return=fgets(s,max_length,new_fp);

  //Now read the output file format.
  f_return=fgets(s,max_length,new_fp);
  sscanf(s,"%d", &results.options.output_choice);

  //Ignore the header line.
  f_return=fgets(s,max_length,new_fp);

  //Now read the number of filenames.
  f_return=fgets(s,max_length,new_fp);
  sscanf(s,"%lld", &k);

  //Use number of files to initialize arrays. First, for unstructured maps:
  if(results.options.output_choice == 1){
    results.titles.resize(k);
    results.units.resize(k);
    results.outputs.resize(k);
    for(i=0;i<k;i++) results.outputs.at(i).resize(grid.lat.size());
    plot_options.output_files.resize(k,filename);
  }//Now for plots:
  else if(results.options.output_choice == 2){
    results.xy.titles.resize(k);
    results.xy.x_units.resize(k);
    results.xy.y_units.resize(k);
    results.xy.legends.resize(k);
    results.xy.x_values.resize(k);
    results.xy.y_values.resize(k);
    plot_options.output_files.resize(k,filename);
  }
  else if(results.options.output_choice == 5){
    results.titles.resize(k);
    results.units.resize(k);
    //latlon.outputs can't be sized until read_output sees grid for the 1st time.
    plot_options.output_files.resize(k,filename);
  }
  else cout<<"WARNING!!! results.options.output_choice "<<results.options.output_choice<<" wasn't recognized!"<<endl;

  //Read filenames into plot_options.
  for(i=0;i<k;i++){
    f_return=fgets(s,max_length,new_fp);
    plot_options.output_files.at(i).append(s);
    //Remove the \n at the end of the line.
    if((npar=plot_options.output_files.at(i).find("\n"))!=(int)string::npos) plot_options.output_files.at(i).swap(plot_options.output_files.at(i).erase(npar,2));
  }
  fclose(new_fp);

  //This variable controls map/plot loading in read_output.
  temp_results.options.output_choice = results.options.output_choice;

  //Now read files and load contents. First, for maps (unstructured or latlon):
  if(results.options.output_choice == 1 or results.options.output_choice == 5){
    encountered_period=0;//Haven't seen a sinusoid, so don't know results.o.sincos
    //Loop through filenames, reading title,units,values into results.
    for(i=0;i<k;i++){
      //Load the titles, units and output from this output file.
      //All titles, units and output are in index 0 b/c just a single file
      read_output(temp_results, plot_options.output_files.at(i), grid);

      //Debug.
      if(results.options.output_choice != 5) if((long long)temp_results.outputs.front().size() != (long long)results.outputs.at(i).size()) cout<<"WARNING! Size of outputs vector #"<<i<<" ("<<results.outputs.at(i).size()<<") doesn't match size of vector from output file ("<<temp_results.outputs.at(i).size()<<")."<<endl;

      //Copy the titles, units, phase format to main results struct.
      results.options.current_phase_format=temp_results.options.current_phase_format;
      results.titles.at(i)=temp_results.titles.front();
      results.units.at(i)=temp_results.units.front();
      //cout<<"load_output() title: "<<results.titles.at(i)<<endl;

      //Use titles to build parameter/type,tide vectors.
      convert_title_to_parameter(results.titles.at(i),period_info);

      //Is this a sinusoid?  If so, record (and compare if possible) sincos.
      if(period_info.parameter_type == 101){
        if(encountered_period == 0){
          results.options.sincos = period_info.sincos;
          encountered_period = 1;
        }
        else{
          if(results.options.sincos != period_info.sincos){
            cout<<"!!!WARNING!!! results.options.sincos = "<<results.options.sincos<<" while period_info.sincos ="<<period_info.sincos<<endl;
          }
        }
      }

      //Strangely, adding the output_type is simple because I'm looping
      //over a list that has two entries for sinusoids.  Adding the
      //parameter_type is below this command- it's a lot more complicated.
      results.options.output_type.push_back(period_info.output_type);

      //Strangely, adding the output_tide is simple because I'm looping
      //over a list that has two entries for sinusoids.  Adding the
      //parameter_tide is below this command- it's a lot more complicated.
      results.options.output_tide.push_back(period_info.tide);

      //Only add title's parameter/type,tide if it's not already there
      if(count(recognized_titles.begin(),recognized_titles.end(),period_info.title)==0){
        //Add the title from period_info so that "M2" is stored rather than
        //"M2 Amp." or "M2 Phase" or "M2 Sine".
        recognized_titles.push_back(period_info.title);
        results.options.parameter.push_back(period_info.period);
        results.options.parameter_type.push_back(period_info.parameter_type);
        results.options.parameter_tide.push_back(period_info.tide);
        cout<<"Loaded output for parameter "<<period_info.title<<"."<<endl;
      }
      if(results.options.output_choice == 1){
        //Loop through grid points to copy output.
        for(j=0;j<(long long)temp_results.outputs.front().size();j++){
          results.outputs.at(i).at(j) = temp_results.outputs.front().at(j);
        }//End of loop through grid points.
      }
      else if(results.options.output_choice == 5){
        //If this is the first parameter, just copy entire latlon struct- grid and output and elev.
        if(i==0){
          results.latlon = temp_results.latlon;
          //But need to resize the outer parameter index- doesn't delete what's already there.
          results.latlon.outputs.resize(k);
        }
        //Otherwise copy just the outputs individually.
        else{
          results.latlon.outputs.at(i).resize(results.latlon.lat.size());
          results.latlon.elev.resize(results.latlon.lat.size());
          for(j=0;j<(long long)results.latlon.lat.size();j++){
            results.latlon.outputs.at(i).at(j).resize(results.latlon.lon.size());
            for(l=0;l<(long long)results.latlon.lon.size();l++){
              results.latlon.outputs.at(i).at(j).at(l) = temp_results.latlon.outputs.at(0).at(j).at(l); 
            }
          }
        }
      }
    }//End of loop through filenames.

    if(1==2){//debug
      cout<<endl<<"results.titles.size() = "<<results.titles.size()<<endl;
      for(j=0;j<(long long)results.titles.size();j++) cout<<"Title("<<j<<")= "<<results.titles.at(j)<<endl;

      cout<<endl<<"param.size() = "<<results.options.parameter.size()<<endl;
      for(j=0;j<(long long)results.options.parameter.size();j++) cout<<"results.opts.param("<<j<<")= "<<results.options.parameter.at(j)<<endl;

      cout<<endl<<"results.opts.param_type.size() = "<<results.options.parameter_type.size()<<endl;
      for(j=0;j<(long long)results.options.parameter_type.size();j++)cout<<"options.parameter_type("<<j<<")= "<<results.options.parameter_type.at(j)<<endl;

      cout<<endl<<"results.opts.param_tide.size() = "<<results.options.parameter_tide.size()<<endl;
      for(j=0;j<(long long)results.options.parameter_tide.size();j++)cout<<"options.parameter_tide("<<j<<")= "<<results.options.parameter_tide.at(j)<<endl;

      cout<<endl<<"output_type.size() = "<<results.options.output_type.size()<<endl;
      for(j=0;j<(long long)results.options.output_type.size();j++) cout<<"output_type("<<j<<")= "<<results.options.output_type.at(j)<<endl;

      cout<<endl<<"output_tide.size() = "<<results.options.output_tide.size()<<endl;
      for(j=0;j<(long long)results.options.output_tide.size();j++) cout<<"output_tide("<<j<<")= "<<results.options.output_tide.at(j)<<endl;

      cout<<endl;
    }//end debug.
  }//Now to read plot filenames:
  //////////////////////////////////////////////////
  //////////////////////////////////////////////////
  else if(results.options.output_choice == 2){
    //Loop through filenames, reading title,units,values into results.
    for(i=0;i<k;i++){
      //cout<<"Reading file #"<<i+1<<endl;
      //Load the titles, units and output from this output file.
      //All titles, units and output are in index 0 b/c just a single file
      read_output(temp_results, plot_options.output_files.at(i), grid);

      //Copy titles and units to results.titles/units.
      results.xy.titles.at(i)=temp_results.xy.titles.front();
      results.xy.x_units.at(i)=temp_results.xy.x_units.front();
      results.xy.y_units.at(i)=temp_results.xy.y_units.front();

      //Resize # of dataseries in this plot.
      results.xy.x_values.at(i).resize(temp_results.xy.x_values.front().size());
      results.xy.y_values.at(i).resize(temp_results.xy.x_values.front().size());
      if(temp_results.xy.legends.front().size() > 0) results.xy.legends.at(i).resize(temp_results.xy.x_values.front().size());

      //Loop through dataseries to copy output,legends.
      for(j=0;j<(long long)temp_results.xy.x_values.front().size();j++){

        //cout<<"Reading line #"<<j+1<<endl;
        if(temp_results.xy.legends.front().size() > 0) results.xy.legends.at(i).at(j) = temp_results.xy.legends.front().at(j);

        //Resize # of data points in this series.
        results.xy.x_values.at(i).at(j).resize(temp_results.xy.x_values.front().at(j).size());
        results.xy.y_values.at(i).at(j).resize(temp_results.xy.x_values.front().at(j).size());

        //Loop through points in this series to copy x,y values.
        for(l=0;l<(long long)temp_results.xy.x_values.front().at(j).size();l++){
          results.xy.x_values.at(i).at(j).at(l) = temp_results.xy.x_values.front().at(j).at(l);
          results.xy.y_values.at(i).at(j).at(l) = temp_results.xy.y_values.front().at(j).at(l);
        }
      }//End of loop through dataseries.
    }//End of loop through filenames.
  }//End of loading plot files.
  else cout<<"WARNING!!! results.options.output_choice "<<results.options.output_choice<<" wasn't recognized!"<<endl;
}

void read_output(results_s &results, string filename, grid_s &grid){
  /**********************************************************************
  Purpose: This function reads data from a single output file. The
          filename is passed to this function as a string.

          It reads the output in the file, which must be in the same
          format that write_output() makes. The output is stored in the
          second argument, a copy of analysis results.

          The grid structure is passed to this function so that
          read_output can verify that the output file being read belongs
          on the grid that is being used in main().
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)

          string filename - holds filepath and name of output file.
  ***********************************************************************
  Output:
          No output, but it does modify the results structure to
          hold the output from the filename specified.
  **********************************************************************/
  long long i,j,k,l;//Counters
  FILE *new_fp;
  char s[max_length];
  double temp_double,onelat,onelon,oneelev,minlon,maxlon,minlat,maxlat,deltalat,deltalon;
  string temp;
  vector<double> temp_vector;//Used to push_back vector of vectors
  int pos;

  char* f_return;//Used to stop fgets return value warning.

  long long temp_long;

  int latlon = 0;//Assume this isn't a latlon file until told otherwise. Compare to options.output_choice.
  //Clear vectors.
  results_s empty_results;
  empty_results.options.output_choice = results.options.output_choice;
  results = empty_results;

  //Load data from output file.
  new_fp = fopen(filename.c_str(),"r");

  switch(results.options.output_choice){
    default: cout<<"!!!!WARNING!!!!!! results.options.output_choice "<<results.options.output_choice<<" isn't recognized."<<endl;
    //Is it a map file with a master file called output_filenames.txt?
    //Unstructured (type 1) and latlon (type 5) both use this code.
    case 1:
    case 5:{
      i=0;
      //Read the file line by line.
      while(fgets(s,max_length,new_fp)!=NULL){
        i+=2;//Just read title AND value.  Not valid after unrecognized line!
        //Read odd numbered lines to determine what the next line is going
        //to be, then load it into the correct variable.
        if(!strcmp(s,"Main Title:\n")){
          f_return=fgets(s,max_length,new_fp);
          results.titles.push_back(s);
          //Remove the \n at the end of the line.
          if((pos=results.titles.front().find("\n"))!=(int)string::npos){
            results.titles.front().swap(results.titles.front().erase(pos,2));
          }
        }
        else if(!strcmp(s,"Units:\n")){
          f_return=fgets(s,max_length,new_fp);
          results.units.push_back(s);
          //Remove the \n at the end of the line.
          if((pos=results.units.front().find("\n"))!=(int)string::npos){
            results.units.front().swap(results.units.front().erase(pos,2));
          }
        }
        else if(!strcmp(s,"Phase format (see current_phase_format in definitions.hpp):\n")){
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%d", &results.options.current_phase_format);
        }
        else if(!strcmp(s,"Error bar:\n")){
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%lf", &temp_double);
          results.error_bars.push_back(temp_double);
        }
        else if(!strcmp(s,"Number of latitudes, then 1st and last latitudes:\n")){
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%lld", &temp_long);
          latlon = 1;
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%lf", &minlat);
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%lf", &maxlat);
          deltalat = (maxlat - minlat)/((double)temp_long-1);
          for(j=0;j<temp_long;j++) results.latlon.lat.push_back(minlat+j*deltalat);
        }
        else if(!strcmp(s,"Number of longitudes, then 1st and last longitudes:\n")){
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%lld", &temp_long);
          latlon = 1;
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%lf", &minlon);
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%lf", &maxlon);
          deltalon = (maxlon - minlon)/((double)temp_long-1);
          for(j=0;j<temp_long;j++) results.latlon.lon.push_back(minlon+j*deltalon);
        }
        else if(!strcmp(s,"Mask value (not physical, shouldn't be converted to/from sin/cos):\n")){
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%lf", &temp_double);
          results.latlon.mask = temp_double;
        }
        else if(!strcmp(s,"Map data (after header line):\n")){
          //Ignore header line.
          f_return=fgets(s,max_length,new_fp);
          if(latlon == 0){
            if(results.options.output_choice == 5) cout<<"WARNING! LATLON OUTPUT FILE EXPECTED, BUT DIDN'T FIND LATITUDE OR LONGITUDE RANGES SO THIS IS PROBABLY AN UNSTRUCTURED MAP!!"<<endl;
            //Load output at each grid point.
            results.outputs.push_back(temp_vector);
            j=0;
            while(fgets(s,max_length,new_fp)!=NULL){
              sscanf(s,"%lf %lf %lf %lf", &onelat, &onelon, &temp_double,&oneelev);
              if(fabs(onelat - grid.lat.at(j)) > 1E-4) cout<<"Latitude in output file at index "<<j<<" ("<<fixed<<setprecision(4)<<onelat<<") doesn't match grid ("<<grid.lat.at(j)<<")!"<<endl;
              if(fabs(onelon - grid.lon.at(j)) > 1E-4) cout<<"Longitude in output file at index "<<j<<" ("<<fixed<<setprecision(4)<<onelon<<") doesn't match grid ("<<grid.lon.at(j)<<")!"<<endl;
              results.outputs.front().push_back(temp_double);
              i++;//Another line read.
              j++;//Another output value read.
            }
          }
          else{
            if(results.options.output_choice != 5) cout<<"WARNING! LATLON OUTPUT FILE WASN'T EXPECTED, BUT LATITUDE OR LONGITUDE RANGES WERE FOUND SO THIS IS PROBABLY A LATLON MAP!!"<<endl;
            //Resize latlon outputs multivector to hold all grid points.
            results.latlon.outputs.resize(1);
            results.latlon.outputs.at(0).resize(results.latlon.lat.size());
            results.latlon.elev.resize(results.latlon.lat.size());
            for(l=0;l<(long long)results.latlon.lat.size();l++){
              results.latlon.outputs.at(0).at(l).resize(results.latlon.lon.size());
              results.latlon.elev.at(l).resize(results.latlon.lon.size());
            }
            //Load output at each grid point.
            for(j=0;j<(long long)results.latlon.lat.size();j++){
              for(l=0;l<(long long)results.latlon.lon.size();l++){
                f_return=fgets(s,max_length,new_fp);
                sscanf(s,"%lf %lf", &oneelev, &temp_double);
                results.latlon.outputs.front().at(j).at(l) = temp_double;
                results.latlon.elev.at(j).at(l) = oneelev;
              }
            }
            i++;//Another line read.
          }
        }
        else{
          cout<<"Did not recognize line #"<<i<<" in file: "<<filename<<endl;
          i--;//Didn't read data line this time.
        }

      }//End of while loop through file.
      fclose(new_fp);
      break;
    }
    //////////////////////////////////////////////////
    //////////////////////////////////////////////////
    //Is it a plot file?
    case 2:{
      k = -1;//Stores index of current dataseries, so it starts at -1.
      i=0;
      //Read the file line by line.
      while(fgets(s,max_length,new_fp)!=NULL){
        i+=2;//Each if reads title AND value. Not valid after unknown line!
        //Read odd numbered lines to determine what the next line is going
        //to be, then load it into the correct variable.
        if(!strcmp(s,"Main Title:\n")){
          f_return=fgets(s,max_length,new_fp);
          results.xy.titles.push_back(s);
          //Remove the \n at the end of the line.
          if((pos=results.xy.titles.front().find("\n"))!=(int)string::npos){
            results.xy.titles.front().swap(results.xy.titles.front().erase(pos,2));
          }
        }
        else if(!strcmp(s,"X Axis Units:\n")){
          f_return=fgets(s,max_length,new_fp);
          results.xy.x_units.push_back(s);
          //Remove the \n at the end of the line.
          if((pos=results.xy.x_units.front().find("\n"))!=(int)string::npos){
            results.xy.x_units.front().swap(results.xy.x_units.front().erase(pos,2));
          }
        }
        else if(!strcmp(s,"Y Axis Units:\n")){
          f_return=fgets(s,max_length,new_fp);
          results.xy.y_units.push_back(s);
          //Remove the \n at the end of the line.
          if((pos=results.xy.y_units.front().find("\n"))!=(int)string::npos){
            results.xy.y_units.front().swap(results.xy.y_units.front().erase(pos,2));
          }
        }
        else if(!strcmp(s,"Number of dataseries, followed by the dataseries:\n")){
          //Read # of dataseries.
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%lld", &temp_long);
          //Resize legends, x/y value vectors. First, just one file.
          results.xy.legends.resize(1);
          results.xy.x_values.resize(1);
          results.xy.y_values.resize(1);
          //Next, temp_long dataseries.
          results.xy.x_values.front().resize(temp_long);
          results.xy.y_values.front().resize(temp_long);
        }
        else if(!strcmp(s,"****************************New data series:\n")){
          k++;//Found new dataseries.
          i--;//No value to be read here.
          if(results.xy.legends.size() == 0) cout<<"WARNING DURING LOADING!!! LEGENDS VECTOR HAS SIZE 0, IMPLYING THAT THE NUMBER OF DATASERIES LINE WAS NEVER FOUND!!"<<endl;

        }
        else if(!strcmp(s,"Legend for this dataseries:\n")){
          f_return=fgets(s,max_length,new_fp);
          temp = s;

          //Make sure that legends vector is correct size.
          if(k != (long long)results.xy.legends.front().size()) cout<<"WARNING!!! When reading legend, k (number of dataseries read so far) = "<<k<<", while results.xy.legends.front().size() = "<<results.xy.legends.front().size()<<endl;

          //Remove the \n at the end of the line.
          if((pos=temp.find("\n"))!=(int)string::npos){
            temp.swap(temp.erase(pos,2));
          }
          results.xy.legends.front().push_back(temp);
        }
        else if(!strcmp(s,"X,Y pairs (after # of points then the header line):\n")){
          f_return=fgets(s,max_length,new_fp);
          sscanf(s,"%lld", &temp_long);
          results.xy.x_values.front().at(k).resize(temp_long);
          results.xy.y_values.front().at(k).resize(temp_long);

          //Ignore header.
          f_return=fgets(s,max_length,new_fp);

          //Loop through datapoints.
          for(j=0;j<(long long)results.xy.x_values.front().at(k).size();j++){
            f_return=fgets(s,max_length,new_fp);
            sscanf(s,"%lf %lf", &results.xy.x_values.front().at(k).at(j),&results.xy.y_values.front().at(k).at(j));
            //cout<<"k:"<<k<<",j:"<<j<<", "<<s<<", "<<scientific<<results.xy.x_values.front().at(k).at(j)<<", "<<results.xy.y_values.front().at(k).at(j)<<endl;
          }
          //Read 3 header lines, not 2, and read many x,y pairs.
          i += 1+(long long)results.xy.x_values.front().at(k).size();
        }
        else{
          cout<<"Did not recognize line #"<<i<<" in file: "<<filename<<endl;
          i--;//Didn't read data line this time.
        }

      }//End of while loop through file.
      fclose(new_fp);
      //Legends can be entirely absent but if not there must be
      //exactly enough for all dataseries.
      if(((long long)results.xy.legends.front().size() != 0) and ((long long)results.xy.legends.front().size() != k+1)) cout<<"WARNING!!! k+1 = "<<k+1<<", while results.xy.legends.front().size() = "<<results.xy.legends.front().size()<<"."<<endl;
      break;
    }
  }//End of switch-case.
}

void perform_upward_continuation(input_s &input,
                                 grid_s &grid,
                                 results_s &results){
  /**********************************************************************
  Purpose: This function takes precomputed output files and computes their
          effects on input pts.  The times and positions of the input are
          unchanged but the accels are over-written.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE
  long long i,j,k,l;
  vector<double> sep_vec;//Unit vector - points from GRACE A to B.
  sep_vec.resize(3,0.0);
  double distanceAB;//between GRACE A and B.
  vector<double> grid2A;//vector that points from grid pt to GRACE A.
  grid2A.resize(3,0.0);
  vector<double> grid2B;//vector that points from grid pt to GRACE B.
  grid2B.resize(3,0.0);
  double distanceA,distanceB;//between grid pt and GRACE A, GRACE B.
  double a_sub_mj;//along-track acc w.r.t. mascon j's (current) mass.

  //Initialize variables for notification function for time remaining.
  notification_s notification;
  //1st notification occurs after two loops, but it usually won't print
  //anything because not enough total_time will have passed (first_interval).
  //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
  notification.interval=2;
  //Desired min and max intervals in seconds b/w notifications.
  notification.first_interval=10;
  notification.min_interval=1*3600, notification.max_interval=2*3600;
  //cout<<"CHANGE UPCONT NOTIF INTERVAL BACK!!!"<<endl;
  //notification.min_interval=30, notification.max_interval=90;
  notification.start_time=-1; notification.current_time=-1;
  notification.previous_time=-1;//Needs to be initialized
  notification.prefix = results.options.prefix;

  //Disable partial save code here, but init the new variables.
  notification.starting_index = 0;
  notification.partial_saves_enabled = 0;
  //Used to size notification intervals.
  notification.previous_i=notification.starting_index;
  notification.num_loops = input.time.size()-notification.starting_index;
  //notification.interval will not be allowed to go above this number
  //so that at LEAST a certain number of messages are seen.
  notification.max_notif_interval=(long long)(notification.num_loops/4);
  //Dummy variables for notify().
  least_squares_s dummy_ls;

  //Used to calculate cos/sin(omega*t) at each input time for efficiency.
  //vector<double> sinvalues,cosvalues;
  //Used to move the switch-case statements completely outside of the grid
  //loop.  There are output_type.size() entries, each multiplies a_sub_mj.
  vector<double> param_coefs;

  //Used for converting timespans in seconds to human readable units.
  timespan_conversion_s timespan_conversion;

  //kahan_s kahan;//Temp for testing kahan summation in disk upcont.

  //Completely wipe accel vector, but put it back to the same size!
  i=(long long)input.accel.size(); input.accel.clear(); input.accel.resize(i,0);
  input.tides_accel.clear(); input.tides_accel.resize(i,0);
  input.nontides_accel.clear(); input.nontides_accel.resize(i,0);

  //Initialized outside of loop for efficiency.
  param_coefs.clear();
  param_coefs.resize(results.options.output_type.size(),0.0);

  //Convert all amp/phase output to sin/cos so upward algorithm is simpl(er)
  convert_results_from_ampphase_to_sincos(results);

  switch(results.options.h_matrix_choice){
    default: cout<<"!!!!WARNING!!!!!! Choice "<<results.options.h_matrix_choice<<" doesn't have an associated case statement in perform_upward_continuation()."<<endl;
    ///////////////////////////////////////////////////////////////////////
    //Cases 1,101 - haven't needed to use zone of inf. in upward_cont
    ///////////////////////////////////////////////////////////////////////
    case 1://Need to remember to add any further h_matrix_choice here!
    case 101:{//Use results and params and input times/positions to generate
            //sim input file by modelling potential due to all grid pts
            //and all params as pt. masses (using grid.area to go from cm
            //to kg) with Newtonian gravity.

      //Loop through input points- this is the outer loop because
      //the sep_vec- vector between the satellites, is calculated
      //only once per input time this way.
      notification.start_time = time(NULL);
      for(i=notification.starting_index;i<(long long)input.time.size();i++){

        notify(notification,i,dummy_ls,input,results);//Periodically print time remaining.

        //Calculate sep_vec for this time.
        sep_vec.front() = input.xB.at(i) - input.xA.at(i);
        sep_vec.at(1) = input.yB.at(i) - input.yA.at(i);
        sep_vec.at(2) = input.zB.at(i) - input.zA.at(i);

        //Normalize sep_vec.
        distanceAB = sqrt(pow(sep_vec.front(),2)+pow(sep_vec.at(1),2)+pow(sep_vec.at(2),2));
        sep_vec.front() = sep_vec.front()/distanceAB;
        sep_vec.at(1) = sep_vec.at(1)/distanceAB;
        sep_vec.at(2) = sep_vec.at(2)/distanceAB;

        //Calculate coefficients that are parameter and time specific
        //but not tied to particular grid points.
        //Ex: constant's coefficient is just "1", trend is the input time
        //multiplied by a conversion factor to make the output in cm/year.
        //The type 101 parameters' coefficients are sin/cos(omega*t).
        //The result is a vector with output_type.size() entries- periods count twice!
        j=0;//Index of output vector - periods count twice.
        for(k=0;k<(long long)results.options.parameter.size();k++){
          //New parameter types should be added here.
          if(results.options.parameter_type.at(k)==101){//Harmonic
            param_coefs.at(j)=cos(results.omegas.at(k)*(input.time.at(i)));
            j++;
            param_coefs.at(j)=sin(results.omegas.at(k)*(input.time.at(i)));
            j++;
          }
          else if(results.options.parameter_type.at(k)==0){//Const
            param_coefs.at(j) = 1.0; j++;
          }
          else if(results.options.parameter_type.at(k)<100){//Polynomial
            //Because pow(±0,y) returns ±∞ and raises the divide-by-zero floating-point exception for y an odd integer.
            if(input.time.at(i) != 0) param_coefs.at(j)=pow((input.time.at(i))*inverse_seconds_in_year,results.options.parameter_type.at(k));//Partial of n'th polynomial term.
            else param_coefs.at(j)=0.0;//Won't ever return ±∞ or raise an exception.
            j++;
          }
          else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.sf3f5"<<endl;
        }//End of loop through parameters.

        //Loop through grid- this is the next loop because this way
        //the distance from the input point(s) to each grid point is only
        //calculated once for each satellite.
        for(j=0;j<(long long)grid.lat.size();j++){
          //Calculate vector/distance from grid point to input point A,B.
          //Convert from km to m.
          grid2A.front() = 1000.0*(input.xA.at(i) - grid.x.at(j));
          grid2A.at(1) = 1000.0*(input.yA.at(i) - grid.y.at(j));
          grid2A.at(2) = 1000.0*(input.zA.at(i) - grid.z.at(j));
          grid2B.front() = 1000.0*(input.xB.at(i) - grid.x.at(j));
          grid2B.at(1) = 1000.0*(input.yB.at(i) - grid.y.at(j));
          grid2B.at(2) = 1000.0*(input.zB.at(i) - grid.z.at(j));

          //Also calculate distances.  Already converted from km to m.
          distanceA = sqrt(pow(grid2A.front(),2)+pow(grid2A.at(1),2)+pow(grid2A.at(2),2));
          distanceB = sqrt(pow(grid2B.front(),2)+pow(grid2B.at(1),2)+pow(grid2B.at(2),2));

          //Along-track acc w.r.t. mascon j's (current) mass.
          //Notice lack of 1E9 factor that forces input accels to be in
          //units of meters/second^2.  Also notice the cm2kg conversion
          //(from define_grid() ).
          a_sub_mj = Big_G*grid.cm2kg.at(j)*((grid2A.front()*sep_vec.front()+grid2A.at(1)*sep_vec.at(1)+grid2A.at(2)*sep_vec.at(2))/pow(distanceA,3) - (grid2B.front()*sep_vec.front()+grid2B.at(1)*sep_vec.at(1)+grid2B.at(2)*sep_vec.at(2))/pow(distanceB,3));
          //Now loop through all params, const, trend, sine, cosine..
          for(k=0;k<(long long)results.options.output_type.size();k++){
            input.accel.at(i) += a_sub_mj*param_coefs.at(k)*results.outputs.at(k).at(j);
            //Only add this parameter to tides if k'th param is a tide.
            if(results.options.output_tide.at(k) == 1) input.tides_accel.at(i) += a_sub_mj*param_coefs.at(k)*results.outputs.at(k).at(j);
            //Only add this param to nontides if k'th param is not a tide.
            else if(results.options.output_tide.at(k) == 0) input.nontides_accel.at(i) += a_sub_mj*param_coefs.at(k)*results.outputs.at(k).at(j);
            else cout<<"!!WARNING!! results.options.output_tide["<<k<<"] = "<<results.options.output_tide.at(k)<<" which is not recognized!"<<endl;
          }//End of loop through parameters.
        }//End of loop through grid points
        //if(i==0) cout<<"End o input loop, input.accel[0]: "<<setprecision(20)<<setw(23)<<input.accel.at(i)<<endl;

      }//End of loop through input.

      break;
    }
    ///////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////
    case 2:{//Use results and params and input times/positions to generate
            //sim input file by modelling potential due to all grid pts
            //and all params as disk masses (using grid.area to go from cm
            //to kg) with Newtonian gravity.
      cout<<"!!!!!WARNING!!!!! HAVEN'T CHANGED TYPE 2 SO THAT IT CAN HANDLE TIDE AND NON TIDE ACCELS!!!  Also: for(j=0;j<1/*grid.lat.size()*/;j++){//Only NP (j==0) works anyway!"<<endl;
      //remember about area and cm2kg - which are similar but used in
      //a unit conversion sense only in the code.
      double radius, radius_angle, radius_cos;
      //radius = grid.options.lat_spacing;
      radius = results.options.disk_radius;
      radius_angle = atan2(radius,planet_polar_radius);
      radius_cos = cos(radius_angle);
      cout<<"radius, radius_angle, radius_cos: "<<radius<<", "<<radius_angle<<", "<<radius_cos<<endl;
      cout<<"nmax: "<<results.options.nmax<<endl;

      //Precalc Legendre polynomials of cos(alpha) as well as denominator.
      //Legendre polynomials need to be calced to nmax+1
      vector<double> legendre_cos_alpha;
      legendre_cos_alpha.resize(results.options.nmax+1);
      for(i=0;i<=results.options.nmax;i++) legendre_cos_alpha.at(i) = gsl_sf_legendre_Pl(i, radius_cos);

      vector<double> gamma_coefs;
      double gamma_denom;
      //1E6 converts from km^2 to m^2.
      gamma_denom = 4.0*pi*1E6*pow(planet_avg_radius,2)*(1-radius_cos);
      gamma_coefs.resize(results.options.nmax);
      //1E6 converts from km^2 to m^2.
      gamma_coefs.front() = 1/(4.0*pi*1E6*pow(planet_avg_radius,2));
      for(i=1;i<results.options.nmax;i++) gamma_coefs.at(i) = (legendre_cos_alpha.at(i-1)-legendre_cos_alpha.at(i+1))/gamma_denom;

      //Find spherical components of sep_vec at GRACE A and B.
      vector <double> spherical_sepvectorA, spherical_sepvectorB;
      //3 components: radius, theta (polar), phi (azimuthal).
      spherical_sepvectorA.resize(3,0); spherical_sepvectorB.resize(3,0);

      //Quick hack for even more speed!
      //Notice lack of 1E9 factor that forces input accels to be in
      //units of meters/second^2.
      double lots_o_constants_and_cm2kg0 = 4*pi*Big_G*grid.cm2kg.front();

      //Compute the upper triangle of the symmetric matrix H^T*H.
      //Loop through input points- this is the outer loop because
      //the sep_vec- vector between the satellites, is calculated
      //only once per input time this way.
      notification.start_time = time(NULL);
      for(i=notification.starting_index;i<(long long)input.time.size();i++){

        notify(notification,i,dummy_ls,input,results);//Periodically print time remaining.

        //Calculate sep_vec for this time.
        sep_vec.front() = input.xB.at(i) - input.xA.at(i);
        sep_vec.at(1) = input.yB.at(i) - input.yA.at(i);
        sep_vec.at(2) = input.zB.at(i) - input.zA.at(i);

        //Normalize sep_vec.
        distanceAB = sqrt(pow(sep_vec.front(),2)+pow(sep_vec.at(1),2)+pow(sep_vec.at(2),2));
        sep_vec.front() = sep_vec.front()/distanceAB;
        sep_vec.at(1) = sep_vec.at(1)/distanceAB;
        sep_vec.at(2) = sep_vec.at(2)/distanceAB;

        //After normalizing, convert distanceAB from km to m.
        //NOT NECESSARY, BECAUSE DISTANCEAB IS NEVER USED AGAIN!
        //distanceAB = distanceAB*1000.0;

        double radiusA = sqrt(pow(input.xA.at(i),2)+pow(input.yA.at(i),2)+pow(input.zA.at(i),2));
        double radiusB = sqrt(pow(input.xB.at(i),2)+pow(input.yB.at(i),2)+pow(input.zB.at(i),2));

        double phiA = atan2(input.yA.at(i),input.xA.at(i));
        double phiB = atan2(input.yB.at(i),input.xB.at(i));

        double thetaA = atan2(sqrt(pow(input.yA.at(i),2)+pow(input.xA.at(i),2)),input.zA.at(i));
        double thetaB = atan2(sqrt(pow(input.yB.at(i),2)+pow(input.xB.at(i),2)),input.zB.at(i));

        //For Legendre functions.
        double cos_thetaA = cos(thetaA);
        double cos_thetaB = cos(thetaB);

        //For theta component of potential gradient.
        double sin_thetaA = sin(thetaA);
        double sin_thetaB = sin(thetaB);

        //Precalc Legendre polynomials of cos(thetaA,B).
        //Legendre polynomials need to be calced to nmax+1
        vector<double> legendre_cos_thetaA,legendre_cos_thetaB;
        legendre_cos_thetaA.resize(results.options.nmax+1);
        legendre_cos_thetaB.resize(results.options.nmax+1);
        for(k=0;k<=results.options.nmax;k++){
          legendre_cos_thetaA.at(k) = gsl_sf_legendre_Pl(k, cos_thetaA);
          legendre_cos_thetaB.at(k) = gsl_sf_legendre_Pl(k, cos_thetaB);
        }

        //Radial components.
        spherical_sepvectorA.front() = sep_vec.front()*sin_thetaA*cos(phiA)+sep_vec.at(1)*sin_thetaA*sin(phiA)+sep_vec.at(2)*cos_thetaA;
        spherical_sepvectorB.front() = sep_vec.front()*sin_thetaB*cos(phiB)+sep_vec.at(1)*sin_thetaB*sin(phiB)+sep_vec.at(2)*cos_thetaB;

        //Theta (polar) components.
        spherical_sepvectorA.at(1) = sep_vec.front()*cos_thetaA*cos(phiA)+sep_vec.at(1)*cos_thetaA*sin(phiA)-sep_vec.at(2)*sin_thetaA;
        spherical_sepvectorB.at(1) = sep_vec.front()*cos_thetaB*cos(phiB)+sep_vec.at(1)*cos_thetaB*sin(phiB)-sep_vec.at(2)*sin_thetaB;

        //Phi (azimuthal) components.  DON'T NEED UNTIL HAVE >1 MASCON!!!!
        //spherical_sepvectorA.at(1) = 1.0;

        //Calculate coefficients that are parameter and time specific
        //but not tied to particular grid points.
        //Ex: constant's coefficient is just "1", trend is the input time
        //multiplied by a conversion factor to make the output in cm/year.
        //The type 101 parameters' coefficients are sin/cos(omega*t).
        //The result is a vector with output_type.size() entries- periods count twice!
        j=0;//Index of output vector - periods count twice.
        for(k=0;k<(long long)results.options.parameter.size();k++){
          //New parameter types should be added here.
          if(results.options.parameter_type.at(k)==101){//Harmonic
            param_coefs.at(j)=cos(results.omegas.at(k)*(input.time.at(i)));
            j++;
            param_coefs.at(j)=sin(results.omegas.at(k)*(input.time.at(i)));
            j++;
          }
          else if(results.options.parameter_type.at(k)==0){//Const
            param_coefs.at(j) = 1.0; j++;
          }
          else if(results.options.parameter_type.at(k)<100){//Polynomial
            //Because pow(±0,y) returns ±∞ and raises the divide-by-zero floating-point exception for y an odd integer.
            if(input.time.at(i) != 0) param_coefs.at(j)=pow((input.time.at(i))*inverse_seconds_in_year,results.options.parameter_type.at(k));//Partial of n'th polynomial term.
            else param_coefs.at(j)=0.0;//Won't ever return ±∞ or raise an exception.
            j++;
          }
          else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type.at(k)<<" was not recognized.d"<<endl;
        }//End of loop through parameters.

        //Loop through grid- this is the next loop because this way
        //the distance from the input point(s) to each grid point is only
        //calculated once for each satellite.
        for(j=0;j<1/*(long long)grid.lat.size()*/;j++){//Only NP (j==0) works anyway!

          //Now deriv of along-track acc w.r.t. mascon j's (current) mass.
          //Loop through all n from 0 to nmax to get all degrees of Legendre
          a_sub_mj = 0.0;

          //Initialize kahan- zero sum and c inside kahan struct.
          //kahan.init = 1;

          //Need to do k=0 first because polar components have derivatives
          //of legendre functions that are zero when k=0 but the general
          //formula refers to "k-1" so the formula can't be applied to k=0.
          k=0;

          //This is calculated nmax times, so be efficient about it.
          double orig_radial_factorA = planet_polar_radius/radiusA;
          double orig_radial_factorB = planet_polar_radius/radiusB;
          double radial_factorA = pow(orig_radial_factorA,2);
          double radial_factorB = pow(orig_radial_factorB,2);

          //Radial parts of potential gradient at A,B dotted w/ sep vector
          a_sub_mj += spherical_sepvectorB.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorB*legendre_cos_thetaB.at(k));
          a_sub_mj -= spherical_sepvectorA.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorA*legendre_cos_thetaA.at(k));

          //Same as above but using Kahan summation.
          //kahan.x = spherical_sepvectorB.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorB*legendre_cos_thetaB.at(k)) - spherical_sepvectorA.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorA*legendre_cos_thetaA.at(k));
          //ksum(kahan);

          //Update radial factors for next (k=1) degree of Legendre expansn.
          radial_factorA *= orig_radial_factorA;
          radial_factorB *= orig_radial_factorB;

          for(k=1;k<results.options.nmax;k++){
            //Radial parts of potential gradient at A,B dotted w/ sep vector
            a_sub_mj += spherical_sepvectorB.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorB*legendre_cos_thetaB.at(k));
            a_sub_mj -= spherical_sepvectorA.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorA*legendre_cos_thetaA.at(k));
            //Polar components (azimuthal not needed yet).
            a_sub_mj += spherical_sepvectorB.at(1)*(gamma_coefs.at(k)/sin_thetaB*(double)(k)/(double)(2*k+1)*radial_factorB*(cos_thetaB*legendre_cos_thetaB.at(k) - legendre_cos_thetaB.at(k-1)));
            a_sub_mj -= spherical_sepvectorA.at(1)*(gamma_coefs.at(k)/sin_thetaA*(double)(k)/(double)(2*k+1)*radial_factorA*(cos_thetaA*legendre_cos_thetaA.at(k) - legendre_cos_thetaA.at(k-1)));

            //Same as above but using Kahan summation.
            //kahan.x = spherical_sepvectorB.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorB*legendre_cos_thetaB.at(k)) + spherical_sepvectorB.at(1)*(gamma_coefs.at(k)/sin_thetaB*(double)(k)/(double)(2*k+1)*radial_factorB*(cos_thetaB*legendre_cos_thetaB.at(k) - legendre_cos_thetaB.at(k-1))) - spherical_sepvectorA.front()*(-gamma_coefs.at(k)*(double)(k+1)/(double)(2*k+1)*radial_factorA*legendre_cos_thetaA.at(k)) - spherical_sepvectorA.at(1)*(gamma_coefs.at(k)/sin_thetaA*(double)(k)/(double)(2*k+1)*radial_factorA*(cos_thetaA*legendre_cos_thetaA.at(k) - legendre_cos_thetaA.at(k-1)));
            //ksum(kahan);

            //Update radial factors for next loop.
            radial_factorA *= orig_radial_factorA;
            radial_factorB *= orig_radial_factorB;
          }
          //I've factored out some consts as well as old business:
          //Notice lack of 1E9 that allows input accels to be in units of
          //nanometers/second^2.  Also notice the cm2kg conversion(grid() ).
          //db lots_o_constants_and_cm2kg0 = 4*pi*Big_G*grid.cm2kg.front()
          a_sub_mj *= lots_o_constants_and_cm2kg0;
          //kahan.sum *= lots_o_constants_and_cm2kg0;

          //double kahan_temp=0.0;
          //Now loop through all params, const, trend, sine, cosine..
          for(k=0;k<(long long)results.options.output_type.size();k++){
            input.accel.at(i) += a_sub_mj*param_coefs.at(k)*results.outputs.at(k).at(j);
            //kahan_temp += kahan.sum*param_coefs.at(k)*results.outputs.at(k).at(j);
          }//End of loop through parameters.
          //cout<<"Simple summation gives input.accel["<<i<<"] = "<<setw(20)<<setprecision(15)<<input.accel.at(i)*1E9<<" nm/s^2."<<endl;
          //cout<<"Kahan summation gives input.accel["<<i<<"] = "<<setw(20)<<setprecision(15)<<kahan_temp*1E9<<" nm/s^2."<<endl<<endl;
        }//End of loop through grid points
        //if(i==0) cout<<"End o input loop, input.accel[0]: "<<setprecision(20)<<setw(23)<<input.accel.at(i)<<endl;
      }//End of loop through input.

      break;
    }
    ///////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////
    case 202://STL not needed in upcont yet!
    case 201:{//GSL format, pt masses.  Uses supporting grid.

      cout<<results.options.prefix<<": Upward continuing using supporting grid of type "<<grid.options.support_type<<", lat_multiplier "<<grid.options.support_lat_multiplier<<" and lon_multiplier "<<grid.options.support_lon_multiplier<<endl;
      //Loop through input points- this is the outer loop because
      //the sep_vec- vector between the satellites, is calculated
      //only once per input time this way.
      notification.start_time = time(NULL);
      for(i=notification.starting_index;i<(long long)input.time.size();i++){

        notify(notification,i,dummy_ls,input,results);//Periodically print time remaining.

        //Calculate sep_vec for this time.
        sep_vec[0] = input.xB[i] - input.xA[i];
        sep_vec[1] = input.yB[i] - input.yA[i];
        sep_vec[2] = input.zB[i] - input.zA[i];

        //Normalize sep_vec.
        distanceAB = sqrt(pow(sep_vec[0],2)+pow(sep_vec[1],2)+pow(sep_vec[2],2));
        sep_vec[0] = sep_vec[0]/distanceAB;
        sep_vec[1] = sep_vec[1]/distanceAB;
        sep_vec[2] = sep_vec[2]/distanceAB;

        //Calculate coefficients that are parameter and time specific
        //but not tied to particular grid points.
        //Ex: constant's coefficient is just "1", trend is the input time
        //multiplied by a conversion factor to make the output in cm/year.
        //The type 101 parameters' coefficients are sin/cos(omega*t).
        //The result is a vector with output_type.size() entries- periods count twice!
        j=0;//Index of output vector - periods count twice.
        for(k=0;k<(long long)results.options.parameter.size();k++){
          //New parameter types should be added here.
          if(results.options.parameter_type[k]==101){//Harmonic
            double current_phase = results.omegas[k]*(input.time[i]);
            param_coefs[j]=cos(current_phase);
            j++;
            param_coefs[j]=sin(current_phase);
            j++;
          }
          else if(results.options.parameter_type[k]==0){//Const
            param_coefs[j] = 1.0; j++;
          }
          else if(results.options.parameter_type[k]<100){//Polynomial
            //Because pow(±0,y) returns ±∞ and raises the divide-by-zero floating-point exception for y an odd integer.
            if(input.time[i] != 0) param_coefs[j]=pow((input.time[i])*inverse_seconds_in_year,results.options.parameter_type[k]);//Partial of n'th polynomial term.
            else param_coefs[j]=0.0;//Won't ever return ±∞ or raise an exception.
            j++;
          }
          else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type[k]<<" was not recognized.gj373uydh36"<<endl;
        }//End of loop through parameters.

        //Loop through grid- this is the next loop because this way
        //the distance from the input point(s) to each grid point is only
        //calculated once for each satellite.
        for(j=0;j<(long long)grid.lat.size();j++){
          //Calculate vector/distance from grid point to input point A,B.
          //Convert from km to m.
          grid2A[0] = 1000.0*(input.xA[i] - grid.x[j]);
          grid2A[1] = 1000.0*(input.yA[i] - grid.y[j]);
          grid2A[2] = 1000.0*(input.zA[i] - grid.z[j]);
          grid2B[0] = 1000.0*(input.xB[i] - grid.x[j]);
          grid2B[1] = 1000.0*(input.yB[i] - grid.y[j]);
          grid2B[2] = 1000.0*(input.zB[i] - grid.z[j]);

          //Also calculate distances.  Already converted from km to m.
          distanceA = sqrt(pow(grid2A[0],2)+pow(grid2A[1],2)+pow(grid2A[2],2));
          distanceB = sqrt(pow(grid2B[0],2)+pow(grid2B[1],2)+pow(grid2B[2],2));

          //Along-track acc w.r.t. mascon j's (current) mass.
          //Notice lack of 1E9 factor that forces input accels to be in
          //units of meters/second^2.  Also notice the cm2kg conversion
          //(from define_grid() ).
          a_sub_mj = Big_G*grid.cm2kg[j]*((grid2A[0]*sep_vec[0]+grid2A[1]*sep_vec[1]+grid2A[2]*sep_vec[2])/pow(distanceA,3) - (grid2B[0]*sep_vec[0]+grid2B[1]*sep_vec[1]+grid2B[2]*sep_vec[2])/pow(distanceB,3));

          //Now weight the acceleration based on the support_weight
          //which takes into account the area that this main gr pt
          //covers.
          a_sub_mj *= grid.main_weights[j];

          //Now loop through all params, const, trend, sine, cosine..
          for(k=0;k<(long long)results.options.output_type.size();k++){
            input.accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
            //Only add this parameter to tides if k'th param is a tide.
            if(results.options.output_tide[k] == 1) input.tides_accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
            //Only add this param to nontides if k'th param is not a tide.
            else if(results.options.output_tide[k] == 0) input.nontides_accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
            else cout<<"!!WARNING!! results.options.output_tide["<<k<<"] = "<<results.options.output_tide[k]<<" which is not recognized!"<<endl;
          }//End of loop through parameters.

          //Now loop through support grid points.
          if((long long)grid.support_lat[j].size() > (long long)0) for(l=0;l<(long long)grid.support_lat[j].size();l++){
            //Calculate vector/distance from supporting grid point to input
            //point A,B.  This is okay because the grid2A/B vecs not needed
            //anymore, and will be reset on the next main grid loop anyway.
            //Convert from km to m.
            grid2A[0]=1E3*(input.xA[i] - grid.support_x[j][l]);
            grid2A[1]=1E3*(input.yA[i] - grid.support_y[j][l]);
            grid2A[2]=1E3*(input.zA[i] - grid.support_z[j][l]);
            grid2B[0]=1E3*(input.xB[i] - grid.support_x[j][l]);
            grid2B[1]=1E3*(input.yB[i] - grid.support_y[j][l]);
            grid2B[2]=1E3*(input.zB[i] - grid.support_z[j][l]);

            //Also calculate distances.  Already converted from km to m.
            //Again, these aren't used anymore, and will be reset next loop.
            distanceA = sqrt(pow(grid2A[0],2)+pow(grid2A[1],2)+pow(grid2A[2],2));
            distanceB = sqrt(pow(grid2B[0],2)+pow(grid2B[1],2)+pow(grid2B[2],2));

            //Along-track acc w.r.t. mascon j's (current) mass.
            //Notice lack of 1E9 factor that forces input accels to be in
            //units of meters/second^2.  Also notice the cm2kg conversion
            //(from define_grid() ).
            a_sub_mj = Big_G*grid.cm2kg[j]*((grid2A[0]*sep_vec[0]+grid2A[1]*sep_vec[1]+grid2A[2]*sep_vec[2])/pow(distanceA,3) - (grid2B[0]*sep_vec[0]+grid2B[1]*sep_vec[1]+grid2B[2]*sep_vec[2])/pow(distanceB,3));

            //Now weight the acceleration based on the support_weight
            //which takes into account the area that this support gr pt
            //covers.
            a_sub_mj *= grid.support_weights[j][l];

            //Now loop through all params, const, trend, sine, cosine..
            for(k=0;k<(long long)results.options.output_type.size();k++){
              input.accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
              //Only add this parameter to tides if k'th param is a tide.
              if(results.options.output_tide[k] == 1) input.tides_accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
              //Only add this param to nontides if k'th param is not a tide.
              else if(results.options.output_tide[k] == 0) input.nontides_accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
              else cout<<"!!WARNING!! results.options.output_tide["<<k<<"] = "<<results.options.output_tide[k]<<" which is not recognized!"<<endl;
            }//End of loop through parameters.
          }//End of loop through supporting grid points.
        }//End of loop through grid points
      }//End of loop through input.

      break;
    }
  }//End of switch-case

  //If needed, convert all sin/cos output back to amp/phase
  if(results.conversion_to_sincos == 2) convert_results_from_sincos_to_ampphase(results);

  notification.end_time = time(NULL);//Record time at end of program.
  notification.time_total = notification.end_time - notification.start_time;
  notification.timespan_conversion.seconds=notification.time_total;
  cout<<results.options.prefix<<" upward continuation took"<<sec2human(notification.timespan_conversion)<<endl;
  #endif
}

void perform_upward_continuation2(input_s &input,
                                  grid_s &grid,
                                  results_s &results,
                                  results_s &ntresults,
                                  long long accelmapindex){
  /**********************************************************************
  Purpose: This function takes precomputed output files and computes their
          effects on input pts.  The times and positions of the input are
          unchanged but the accels are over-written.
          A separate soln is used for non-tide-only accels.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Requires GSL libraries.
  #ifdef GSL_HERE
  long long i,j,k,l;
  vector<double> sep_vec;//Unit vector - points from GRACE A to B.
  sep_vec.resize(3,0.0);
  double distanceAB;//between GRACE A and B.
  vector<double> grid2A;//vector that points from grid pt to GRACE A.
  grid2A.resize(3,0.0);
  vector<double> grid2B;//vector that points from grid pt to GRACE B.
  grid2B.resize(3,0.0);
  double distanceA,distanceB;//between grid pt and GRACE A, GRACE B.
  double a_sub_mj;//along-track acc w.r.t. mascon j's (current) mass.

  //Initialize variables for notification function for time remaining.
  notification_s notification;
  //1st notification occurs after two loops, but it usually won't print
  //anything because not enough total_time will have passed (first_interval).
  //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
  notification.interval=2;
  //Desired min and max intervals in seconds b/w notifications.
  notification.first_interval=10;
  notification.min_interval=1*3600, notification.max_interval=2*3600;
  //cout<<"CHANGE UPCONT NOTIF INTERVAL BACK!!!"<<endl;
  //notification.min_interval=30, notification.max_interval=90;
  notification.start_time=-1; notification.current_time=-1;
  notification.previous_time=-1;//Needs to be initialized
  notification.prefix = results.options.prefix;

  //Disable partial save code here, but init the new variables.
  if(accelmapindex < 0) notification.starting_index = 0;
  else notification.starting_index = accelmapindex;

  notification.partial_saves_enabled = 0;
  //Used to size notification intervals.
  notification.previous_i=notification.starting_index;
  notification.num_loops = input.time.size()-notification.starting_index;
  //notification.interval will not be allowed to go above this number
  //so that at LEAST a certain number of messages are seen.
  notification.max_notif_interval=(long long)(notification.num_loops/4);
  //Dummy variables for notify().
  least_squares_s dummy_ls;

  //Used to calculate cos/sin(omega*t) at each input time for efficiency.
  //vector<double> sinvalues,cosvalues;
  //Used to move the switch-case statements completely outside of the grid
  //loop.  There are output_type.size() entries, each multiplies a_sub_mj.
  vector<double> param_coefs;

  //Used for converting timespans in seconds to human readable units.
  timespan_conversion_s timespan_conversion;

  //kahan_s kahan;//Temp for testing kahan summation in disk upcont.

  //Completely wipe accel vectors, but put them back to the same size!
  i=(long long)input.accel.size(); input.accel.clear(); input.accel.resize(i,0);
  input.tides_accel.clear(); input.tides_accel.resize(i,0);
  input.nontides_accel.clear(); input.nontides_accel.resize(i,0);
  input.landtides_accel.clear(); input.landtides_accel.resize(i,0);
  
  //Initialized outside of loop for efficiency.
  param_coefs.clear();
  param_coefs.resize(results.options.output_type.size(),0.0);
  
  //display_output(results, grid,500);

  //Convert all amp/phase output to sin/cos so upward algorithm is simpl(er)
  convert_results_from_ampphase_to_sincos(results);
  convert_results_from_ampphase_to_sincos(ntresults);

  switch(results.options.h_matrix_choice){
    default: cout<<"!!!!WARNING!!!!!! Choice "<<results.options.h_matrix_choice<<" doesn't have an associated case statement in perform_upward_continuation2()."<<endl;
    ///////////////////////////////////////////////////////////////////////
    //Cases 201,202
    ///////////////////////////////////////////////////////////////////////
    case 202://STL not needed in upcont.
    case 201:{//GSL format, pt masses.  Uses supporting grid.

      cout<<results.options.prefix<<": Upward continuing2 using supporting grid of type "<<grid.options.support_type<<", lat_multiplier "<<grid.options.support_lat_multiplier<<" and lon_multiplier "<<grid.options.support_lon_multiplier<<endl;
      //Loop through input points- this is the outer loop because
      //the sep_vec- vector between the satellites, is calculated
      //only once per input time this way.
      notification.start_time = time(NULL);
      for(i=notification.starting_index;i<(long long)input.time.size();i++){

        notify(notification,i,dummy_ls,input,results);//Periodically print time remaining.

        //Calculate sep_vec for this time.
        sep_vec[0] = input.xB[i] - input.xA[i];
        sep_vec[1] = input.yB[i] - input.yA[i];
        sep_vec[2] = input.zB[i] - input.zA[i];

        //Normalize sep_vec.
        distanceAB = sqrt(pow(sep_vec[0],2)+pow(sep_vec[1],2)+pow(sep_vec[2],2));
        sep_vec[0] = sep_vec[0]/distanceAB;
        sep_vec[1] = sep_vec[1]/distanceAB;
        sep_vec[2] = sep_vec[2]/distanceAB;

        //Calculate coefficients that are parameter and time specific
        //but not tied to particular grid points.
        //Ex: constant's coefficient is just "1", trend is the input time
        //multiplied by a conversion factor to make the output in cm/year.
        //The type 101 parameters' coefficients are sin/cos(omega*t).
        //The result is a vector with output_type.size() entries- periods count twice!
        j=0;//Index of output vector - periods count twice.
        for(k=0;k<(long long)results.options.parameter.size();k++){
          //New parameter types should be added here.
          if(results.options.parameter_type[k]==101){//Harmonic
            double current_phase = results.omegas[k]*(input.time[i]);
            param_coefs[j]=cos(current_phase);
            j++;
            param_coefs[j]=sin(current_phase);
            j++;
          }
          else if(results.options.parameter_type[k]==0){//Const
            param_coefs[j] = 1.0; j++;
          }
          else if(results.options.parameter_type[k]<100){//Polynomial
            //Because pow(±0,y) returns ±∞ and raises the divide-by-zero floating-point exception for y an odd integer.
            if(input.time[i] != 0) param_coefs[j]=pow((input.time[i])*inverse_seconds_in_year,results.options.parameter_type[k]);//Partial of n'th polynomial term.
            else param_coefs[j]=0.0;//Won't ever return ±∞ or raise an exception.
            j++;
          }
          else cout<<"!!WARNING!! Parameter type "<<results.options.parameter_type[k]<<" was not recognized.gj373uydh36"<<endl;
        }//End of loop through parameters.

        //Loop through grid- this is the next loop because this way
        //the distance from the input point(s) to each grid point is only
        //calculated once for each satellite.
        for(j=0;j<(long long)grid.lat.size();j++){
          //Calculate vector/distance from grid point to input point A,B.
          //Convert from km to m.
          grid2A[0] = 1000.0*(input.xA[i] - grid.x[j]);
          grid2A[1] = 1000.0*(input.yA[i] - grid.y[j]);
          grid2A[2] = 1000.0*(input.zA[i] - grid.z[j]);
          grid2B[0] = 1000.0*(input.xB[i] - grid.x[j]);
          grid2B[1] = 1000.0*(input.yB[i] - grid.y[j]);
          grid2B[2] = 1000.0*(input.zB[i] - grid.z[j]);

          //Also calculate distances.  Already converted from km to m.
          distanceA = sqrt(pow(grid2A[0],2)+pow(grid2A[1],2)+pow(grid2A[2],2));
          distanceB = sqrt(pow(grid2B[0],2)+pow(grid2B[1],2)+pow(grid2B[2],2));

          //Along-track acc w.r.t. mascon j's (current) mass.
          //Notice lack of 1E9 factor that forces input accels to be in
          //units of meters/second^2.  Also notice the cm2kg conversion
          //(from define_grid() ).
          a_sub_mj = Big_G*grid.cm2kg[j]*((grid2A[0]*sep_vec[0]+grid2A[1]*sep_vec[1]+grid2A[2]*sep_vec[2])/pow(distanceA,3) - (grid2B[0]*sep_vec[0]+grid2B[1]*sep_vec[1]+grid2B[2]*sep_vec[2])/pow(distanceB,3));

          //Now weight the acceleration based on the support_weight
          //which takes into account the area that this main gr pt
          //covers.
          a_sub_mj *= grid.main_weights[j];

          //Now loop through all params, const, trend, sine, cosine..
          for(k=0;k<(long long)results.options.output_type.size();k++){
            if(accelmapindex>=0) ntresults.outputs[0][j]+=a_sub_mj*param_coefs[k]*results.outputs[k][j];
            if(accelmapindex>=0 and j==0) cout<<"At time "<<input.time.at(i)<<", gr #"<<j<<" has a_sub_mj "<<a_sub_mj<<endl;
            if(accelmapindex>=0 and j==0) cout<<"k="<<k<<" and results.outputs[k][j] = "<<results.outputs[k][j]<<endl;
            if(accelmapindex>=0 and j==0) cout<<"At time "<<input.time.at(i)<<", gr #"<<j<<" has height "<<param_coefs[k]*results.outputs[k][j]<<endl;
            //MAKE SURE TO KEEP SYNCED WITH SUPP GR PTS BELOW!
            //Only add this parameter to tides if k'th param is a tide.
            //Note that tides over land aren't included.
            if(results.options.output_tide[k] == 1 and grid.wcover[j] > 0.0){
              input.tides_accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
              input.accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
            }
            //Only add this param to nontides if k'th param is not a tide.
            //Note that this calc uses the separate non-tide solution!
            else if(results.options.output_tide[k] == 0){
              if(accelmapindex < 0) input.nontides_accel[i] += a_sub_mj*param_coefs[k]*ntresults.outputs[k][j];
              //Yes, even the combined accel doesn't include land tides.
              input.accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
            }
            //Now tides over land ARE included.
            else if(results.options.output_tide[k] == 1 and grid.wcover[j] == 0.0){
              input.landtides_accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
            }
          }//End of loop through parameters.

          //Now loop through support grid points.
          if((long long)grid.support_lat[j].size() > (long long)0) for(l=0;l<(long long)grid.support_lat[j].size();l++){
            //Calculate vector/distance from supporting grid point to input
            //point A,B.  This is okay because the grid2A/B vecs not needed
            //anymore, and will be reset on the next main grid loop anyway.
            //Convert from km to m.
            grid2A[0]=1E3*(input.xA[i] - grid.support_x[j][l]);
            grid2A[1]=1E3*(input.yA[i] - grid.support_y[j][l]);
            grid2A[2]=1E3*(input.zA[i] - grid.support_z[j][l]);
            grid2B[0]=1E3*(input.xB[i] - grid.support_x[j][l]);
            grid2B[1]=1E3*(input.yB[i] - grid.support_y[j][l]);
            grid2B[2]=1E3*(input.zB[i] - grid.support_z[j][l]);

            //Also calculate distances.  Already converted from km to m.
            //Again, these aren't used anymore, and will be reset next loop.
            distanceA = sqrt(pow(grid2A[0],2)+pow(grid2A[1],2)+pow(grid2A[2],2));
            distanceB = sqrt(pow(grid2B[0],2)+pow(grid2B[1],2)+pow(grid2B[2],2));

            //Along-track acc w.r.t. mascon j's (current) mass.
            //Notice lack of 1E9 factor that forces input accels to be in
            //units of meters/second^2.  Also notice the cm2kg conversion
            //(from define_grid() ).
            a_sub_mj = Big_G*grid.cm2kg[j]*((grid2A[0]*sep_vec[0]+grid2A[1]*sep_vec[1]+grid2A[2]*sep_vec[2])/pow(distanceA,3) - (grid2B[0]*sep_vec[0]+grid2B[1]*sep_vec[1]+grid2B[2]*sep_vec[2])/pow(distanceB,3));

            //Now weight the acceleration based on the support_weight
            //which takes into account the area that this support gr pt
            //covers.
            a_sub_mj *= grid.support_weights[j][l];

            //Now loop through all params, const, trend, sine, cosine..
            for(k=0;k<(long long)results.options.output_type.size();k++){
              if(accelmapindex>=0) ntresults.outputs[0][j]+=a_sub_mj*param_coefs[k]*results.outputs[k][j];
              //MAKE SURE TO KEEP SYNCED WITH MAIN GR PTS ABOVE!
              //Only add this parameter to tides if k'th param is a tide.
              //Note that tides over land aren't included.
              if(results.options.output_tide[k] == 1 and grid.wcover[j] > 0.0){
                input.tides_accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
                input.accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
              }
              //Only add this param to nontides if k'th param is not a tide.
              //Note that this calc uses the separate non-tide solution!
              else if(results.options.output_tide[k] == 0){
                if(accelmapindex < 0) input.nontides_accel[i] += a_sub_mj*param_coefs[k]*ntresults.outputs[k][j];
                //Yes, even the combined accel doesn't include land tides.
                input.accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
              }
              //Now tides over land ARE included.
              else if(results.options.output_tide[k] == 1 and grid.wcover[j] == 0.0){
                input.landtides_accel[i] += a_sub_mj*param_coefs[k]*results.outputs[k][j];
              }
            }//End of loop through parameters.
          }//End of loop through supporting grid points.
        }//End of loop through grid points
        if(accelmapindex >= 0) break;//Just that one point was enough.
      }//End of loop through input.
      break;
    }
  }//End of switch-case

  //If needed, convert all sin/cos output back to amp/phase
  if(results.conversion_to_sincos == 2) convert_results_from_sincos_to_ampphase(results);

  notification.end_time = time(NULL);//Record time at end of program.
  notification.time_total = notification.end_time - notification.start_time;
  notification.timespan_conversion.seconds=notification.time_total;
  cout<<results.options.prefix<<" upward continuation2 took"<<sec2human(notification.timespan_conversion)<<endl;
  #endif
}

void save_regional_input(string regioninputfilename, input_s &input){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  string region_file(gaiafolder());
  region_file.append(input.options.output_folder);
  region_file.append(regioninputfilename);
  //cout<<"input.options.output_folder: "<<input.options.output_folder<<endl;
  //cout<<"input.epoch: "<<input.epoch<<endl;
  //Open regional input file, write data to disk.
  cout<<"Saving regional input data to "<<region_file<<endl;
  FILE *region_fp;
  region_fp = fopen(region_file.c_str(),"w");
  fprintf(region_fp,"  Main Index        Time   Residual (m/s^2)\n");
  for(i=0;i<(long long)input.time.size();i++){
    fprintf(region_fp,"%12lld%12lld%19.5e\n",input.indices_of_points_in_region.at(i),input.time.at(i)+input.epoch, input.accel.at(i));
  }
  fclose(region_fp);
}

void combine_regional_inputs(input_s &input, grid_s &region){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //These variables are used to search the output folder.
  DIR *outputDIR;
  struct dirent *outputDirEnt;
  string temp;
  char s[max_length];
  long long i,j;
  results_s temp_results;
  //When each filename is read, the region index is extracted from the
  //filename (remember the +1!) and stored here.
  long long current_region;
  //The max region index found in the filenames is recorded here.
  long long num_regions=0;

  //This vector holds region indices for each region's input file.
  vector<long long> file_info;

  vector<string> region_input_filenames;

  //Used to build new input file.
  vector<double> mainaccels;
  vector<long long> maintimes, maindebug;
  long long onetime,temp_long;
  double onelat,onelon,oneaccel,onexA,onexB,oneyA,oneyB,onezA,onezB;
  int oneascending;
  FILE *in_fp, *out_fp;
  string old_file,new_file;
  string absolute_input_folder(gaiafolder());
  absolute_input_folder.append(input.options.output_folder);

  char* f_return;//Used to stop fgets return value warning.

  //Open the input data folder.
  outputDIR = opendir(absolute_input_folder.c_str());

  //Search the output folder for files with names such as
  //"region_*" - these are the files created during parallel processing.
  outputDirEnt = readdir(outputDIR);
  i=0;
  while(outputDirEnt != NULL){
    temp = gaiafolder();
    temp.append(input.options.output_folder);
    strcpy(s,outputDirEnt->d_name);
    temp.append(s);
    //If this filename contains "region_", it needs to be read.
    if(((int)temp.find("region_"))!=(int)string::npos){
      region_input_filenames.push_back(temp);
      //cout<<plot_options.output_files.at(i)<<endl;

      //Read the region index and the parameter index from the filename,
      //store in file_info vector.  Also look for max value of parameter
      //and region to make sure number of regions is right and to resize
      //the results vectors using the number of parameters.
      sscanf(s,"region_%5lld_input.txt", &current_region);
      current_region--;//Filenames are +1 compared to indices!
      if(current_region+1>num_regions) num_regions = current_region+1;//+1
      file_info.push_back(current_region);
      //cout<<"current region: "<<file_info.at(i)<<endl;
      i++;
    }//End of "does this filename contain "region_" " statement.
    outputDirEnt = readdir(outputDIR);
  }//End of while loop through files in outputfolder.

  //Close the main output folder.
  closedir(outputDIR);

  //Compare num_regions to region.lat.size().
  if(num_regions != (long long)region.lat.size()) cout<<"WARNING! num_regions = "<<num_regions<<" and region.lat.size() = "<<region.lat.size()<<endl;

  //Main vectors should have same length as binindices- ENTIRE input!
  maintimes.resize(input.binindices.size(),0);
  mainaccels.resize(input.binindices.size(),0.0);
  maindebug.resize(input.binindices.size(),0);

  //Loop through region input files.
  for(i=0;i<(long long)region_input_filenames.size();i++){

    //Open region input file.
    old_file = region_input_filenames.at(i);
    in_fp = fopen(old_file.c_str(),"r");
    //Warn if file doesn't open correctly.
    if(in_fp == NULL) cout << "The regional input data file, "
                            << old_file << ", failed to open."<<endl;
    else cout << "Loading input data from regional file "
              << old_file << "."<<endl;

    //Ignore header line in region input file.
    f_return=fgets(s,max_length,in_fp);

    //Read indices, times and accels- store input points that were binned
    //into CENTER BINS ONLY in maintimes/accels at indices.
    j=0;
    while(fgets(s,max_length,in_fp)!=NULL){
      j++;
      //Parse line into temp vars.
      sscanf(s,"%lld %lld %le", &temp_long, &onetime, &oneaccel);
      //Center bins only!
      if(count(region.center_grid_indices.at(file_info.at(i)).begin(),region.center_grid_indices.at(file_info.at(i)).end(),input.binindices.at(temp_long)) == 1){
        maintimes.at(temp_long) = onetime;
        mainaccels.at(temp_long) = oneaccel;
        maindebug.at(temp_long)++;
      }
    }//End o loop through old input file.

    //Close region input file.
    fclose(in_fp);
  }//End of loop through region input filenames.

  //debug
  j=0;//Don't let more than 100 warnings print- file becomes too large!
  while(i<(long long)maindebug.size() and j < 100){
    if(maindebug.at(i) != 1){
      cout<<"WARNING!! maindebug["<<i<<"] = "<<maindebug.at(i)<<endl;
      j++;//One more warning printed to the screen
    }
    i++;//One more entry examined.
  }

  //Loop through temporary region filenames, delete them!
  //(Dangerous to do this before writing new input file, but space is at
  //a premium, so I should delete these files now that they're not needed
  //rather than deleting them after the new input file is written.)
  for(i=0;i<(long long)region_input_filenames.size();i++) if(remove(region_input_filenames.at(i).c_str()) != 0) cout<<"Problem deleting temp file "<<region_input_filenames.at(i)<<endl;

  //Open orig input file.
  old_file = gaiafolder();
  old_file.append(input.options.basefolder);
  old_file.append(input.options.filename);
  in_fp = fopen(old_file.c_str(),"r");
  //Warn if file doesn't open correctly.
  if(in_fp == NULL) cout << "The original input data file, " << old_file
                         << ", failed to open."<<endl;
  else cout << "Loading input data from original file "
            << old_file << "."<<endl;

  //Open sim input file from output_folder.
  new_file = gaiafolder();
  new_file.append(input.options.output_folder);
  new_file.append(input.options.output_filename);
  out_fp = fopen(new_file.c_str(),"w");
  //Warn if file doesn't open correctly.
  if(out_fp == NULL) cout << "The sim input data file, " << new_file
                          << ", failed to open."<<endl;
  else cout << "Saving input data to sim file "
            << new_file << "."<<endl;

  //Read header line from orig file.
  f_return=fgets(s,max_length,in_fp);

  //Copy that header file to sim input file to avoid hard-coding it.
  fprintf(out_fp,"%s",s);

  //Loop through lines in old input file - complain if maintime's not equal!
  i=0; j=0;//i - file line.  j - counter to insure that # warnings < 100.
  while(fgets(s,max_length,in_fp)!=NULL and j<100){
    //Parse line into temp vars.
    sscanf(s,ORBIT_READ_FORMAT, &onetime, &onelat, &onelon, &oneaccel, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);
    //Compare times for debug purposes- they should be the same!
    if(maintimes.at(i) != onetime){
      cout<<"!!!!WARNING- original input time is "<<onetime<<" while region time at index "<<i<<" is "<<maintimes.at(i)<<endl;
      j++;
    }
    //Else, write temp vars + mainaccels[i] to new file.
    else fprintf(out_fp,ORBIT_WRITE_FORMAT,onetime, onelat, onelon, mainaccels.at(i), oneascending, onexA, onexB, oneyA, oneyB, onezA, onezB);

    i++;//Another line, another entry in mainaccels.
  }//End o loop through old input file.

  //Close both input files.
  fclose(in_fp); fclose(out_fp);
}

void save_regional_verification_input(string regioninputfilename,
                                      input_s &input){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  string region_file(gaiafolder());
  region_file.append(input.options.output_folder);
  region_file.append(regioninputfilename);
  //cout<<"input.options.output_folder: "<<input.options.output_folder<<endl;
  //cout<<"input.epoch: "<<input.epoch<<endl;
  //Open regional input file, write data to disk.
  cout<<"Saving regional input data to "<<region_file<<endl;
  FILE *region_fp;
  region_fp = fopen(region_file.c_str(),"w");
  fprintf(region_fp,"  Main Index        Time   Combined (m/s^2) Oceantides (m/s^2)   Nontides (m/s^2)  Land tides (m/s^2)\n");
  for(i=0;i<(long long)input.time.size();i++){
    fprintf(region_fp,"%12lld%12lld%19.5e%19.5e%19.5e%19.5e\n",input.indices_of_points_in_region.at(i),input.time.at(i)+input.epoch, input.accel.at(i),input.tides_accel.at(i),input.nontides_accel.at(i),input.landtides_accel.at(i));
  }
  fclose(region_fp);
}

void combine_regional_verification_inputs(input_s &input, grid_s &region){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //These variables are used to search the output folder.
  DIR *outputDIR;
  struct dirent *outputDirEnt;
  string temp;
  char s[max_length];
  long long i,j;
  results_s temp_results;
  //When each filename is read, the region index is extracted from the
  //filename (remember the +1!) and stored here.
  long long current_region;
  //The max region index found in the filenames is recorded here.
  long long num_regions=0;

  //This vector holds region indices for each region's input file.
  vector<long long> file_info;

  vector<string> region_input_filenames;

  //Used to build new input file.
  vector<double> mainaccels,main_tide_accels,main_nontide_accels,main_landtide_accels;
  vector<long long> maintimes, maindebug;
  long long onetime,temp_long;
  double onelat,onelon,oneaccel,onexA,onexB,oneyA,oneyB,onezA,onezB;
  double oneaccel2,oneaccel3,oneaccel4;
  int oneascending;
  FILE *in_fp, *out_fp;
  string old_file,new_file;
  string absolute_input_folder(gaiafolder());
  absolute_input_folder.append(input.options.output_folder);

  char* f_return;//Used to stop fgets return value warning.

  //Open the input data folder.
  outputDIR = opendir(absolute_input_folder.c_str());

  //Search the output folder for files with names such as
  //"region_*" - these are the files created during parallel processing.
  outputDirEnt = readdir(outputDIR);
  i=0;
  while(outputDirEnt != NULL){
    temp = gaiafolder();
    temp.append(input.options.output_folder);
    strcpy(s,outputDirEnt->d_name);
    temp.append(s);
    //If this filename contains "region_", it needs to be read.
    if(((int)temp.find("region_"))!=(int)string::npos){
      region_input_filenames.push_back(temp);
      //cout<<plot_options.output_files.at(i)<<endl;

      //Read the region index and the parameter index from the filename,
      //store in file_info vector.  Also look for max value of parameter
      //and region to make sure number of regions is right and to resize
      //the results vectors using the number of parameters.
      sscanf(s,"region_%5lld_input.txt", &current_region);
      current_region--;//Filenames are +1 compared to indices!
      if(current_region+1>num_regions) num_regions = current_region+1;//+1
      file_info.push_back(current_region);
      //cout<<"current region: "<<file_info.at(i)<<endl;
      i++;
    }//End of "does this filename contain "region_" " statement.
    outputDirEnt = readdir(outputDIR);
  }//End of while loop through files in outputfolder.

  //Close the main output folder.
  closedir(outputDIR);

  //Compare num_regions to region.lat.size().
  if(num_regions != (long long)region.lat.size()) cout<<"WARNING! num_regions = "<<num_regions<<" and region.lat.size() = "<<region.lat.size()<<endl;

  //Main vectors should have same length as binindices- ENTIRE input!
  maintimes.resize(input.binindices.size(),0);
  mainaccels.resize(input.binindices.size(),0.0);
  main_tide_accels.resize(input.binindices.size(),0.0);
  main_nontide_accels.resize(input.binindices.size(),0.0);
  main_landtide_accels.resize(input.binindices.size(),0.0);
  maindebug.resize(input.binindices.size(),0);

  //Loop through region input files.
  for(i=0;i<(long long)region_input_filenames.size();i++){

    //Open region input file.
    old_file = region_input_filenames.at(i);
    in_fp = fopen(old_file.c_str(),"r");
    //Warn if file doesn't open correctly.
    if(in_fp == NULL) cout << "The regional input data file, "
                            << old_file << ", failed to open."<<endl;
    else cout << "Loading input data from regional file "
              << old_file << "."<<endl;

    //Ignore header line in region input file.
    f_return=fgets(s,max_length,in_fp);

    //Read indices, times and accels- store input points that were binned
    //into CENTER BINS ONLY in maintimes/accels at indices.
    j=0;
    while(fgets(s,max_length,in_fp)!=NULL){
      j++;
      //Parse line into temp vars.
      sscanf(s,"%lld %lld %le %le %le %le", &temp_long, &onetime, &oneaccel, &oneaccel2, &oneaccel3, &oneaccel4);
      //Center bins only!
      if(count(region.center_grid_indices.at(file_info.at(i)).begin(),region.center_grid_indices.at(file_info.at(i)).end(),input.binindices.at(temp_long)) == 1){
        maintimes.at(temp_long) = onetime;
        mainaccels.at(temp_long) = oneaccel;
        main_tide_accels.at(temp_long) = oneaccel2;
        main_nontide_accels.at(temp_long) = oneaccel3;
        main_landtide_accels.at(temp_long) = oneaccel4;
        maindebug.at(temp_long)++;
      }
    }//End o loop through old input file.

    //Close region input file.
    fclose(in_fp);
  }//End of loop through region input filenames.

  //debug
  j=0;//Don't let more than 100 warnings print- file becomes too large!
  while(i<(long long)maindebug.size() and j < 100){
    if(maindebug.at(i) != 1){
      cout<<"WARNING!! maindebug["<<i<<"] = "<<maindebug.at(i)<<endl;
      j++;//One more warning printed to the screen
    }
    i++;//One more entry examined.
  }

  //Loop through temporary region filenames, delete them!
  //(Dangerous to do this before writing new input file, but space is at
  //a premium, so I should delete these files now that they're not needed
  //rather than deleting them after the new input file is written.)
  for(i=0;i<(long long)region_input_filenames.size();i++) if(remove(region_input_filenames.at(i).c_str()) != 0) cout<<"Problem deleting temp file "<<region_input_filenames.at(i)<<endl;

  //Open orig input file.
  old_file = gaiafolder();
  old_file.append(input.options.basefolder);
  old_file.append(input.options.filename);
  in_fp = fopen(old_file.c_str(),"r");
  //Warn if file doesn't open correctly.
  if(in_fp == NULL) cout << "The original input data file, " << old_file
                          << ", failed to open."<<endl;
  else cout << "Loading input data from original file "
            << old_file << "."<<endl;

  //Open sim input file from output_folder.
  new_file = gaiafolder();
  new_file.append(input.options.output_folder);
  new_file.append(input.options.output_filename);
  out_fp = fopen(new_file.c_str(),"w");
  //Warn if file doesn't open correctly.
  if(out_fp == NULL) cout << "The sim input data file, " << new_file
                           << ", failed to open."<<endl;
  else cout << "Saving input data to sim file "
            << new_file << "."<<endl;

  //Read header line from orig file.
  f_return=fgets(s,max_length,in_fp);

  //This verification input file has a different header line.
  fprintf(out_fp,"        Time   Combined (m/s^2) Oceantides (m/s^2)   Nontides (m/s^2)  Land tides (m/s^2)\n");

  //Loop through lines in old input file - complain if maintime's not equal!
  i=0; j=0;//i - file line.  j - counter to insure that # warnings < 100.
  while(fgets(s,max_length,in_fp)!=NULL and j<100){
    //Parse line into temp vars.
    sscanf(s,ORBIT_READ_FORMAT, &onetime, &onelat, &onelon, &oneaccel, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);
    //Compare times for debug purposes- they should be the same!
    if(maintimes.at(i) != onetime){
      cout<<"!!!!WARNING- original input time is "<<onetime<<" while region time at index "<<i<<" is "<<maintimes.at(i)<<endl;
      j++;
    }
    //Else, write temp vars + mainaccels[i] to new file.
    else fprintf(out_fp,"%12lld%19.5e%19.5e%19.5e%19.5e\n",onetime, mainaccels.at(i), main_tide_accels.at(i), main_nontide_accels.at(i), main_landtide_accels.at(i));

    i++;//Another line, another entry in mainaccels.
  }//End o loop through old input file.

  //Close both input files.
  fclose(in_fp); fclose(out_fp);
}

void write_input(input_s &input){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  string temp;
  long long i;

  //Used to build new input file.
  FILE *out_fp;
  string new_file;

  //Open sim input file from output_folder.
  new_file = gaiafolder();
  new_file.append(input.options.output_folder);
  new_file.append(input.options.output_filename);
  out_fp = fopen(new_file.c_str(),"w");
  //Warn if file doesn't open correctly.
  if(out_fp == NULL) cout << "The new input data file, " << new_file
                           << ", failed to open."<<endl;
  else cout << "Saving input data to file "
            << new_file << "."<<endl;

  //Print header line.
  fprintf(out_fp,ORBIT_HEADER);

  for(i=0;i<(long long)input.lat.size();i++){
    fprintf(out_fp,ORBIT_WRITE_FORMAT,input.time.at(i)+input.epoch, input.lat.at(i), input.lon.at(i), input.accel.at(i), input.ascending.at(i), input.xA.at(i), input.xB.at(i), input.yA.at(i), input.yB.at(i), input.zA.at(i), input.zB.at(i));

  }//End o loop through input.

  fclose(out_fp);
}

double restrict_degrees(double phase){
  /**********************************************************************
  Input:  phase, in degrees but not always restricted to any domain.
  ***********************************************************************
  Output: phase, in degrees and restricted to [-180,+180] domain.
  **********************************************************************/
  //First modulo phases with abs > 360.
  if(fabs(phase) >= 360.0)
    phase = fmod(phase,360.0);

  //Now restrict phases to +-180.
  if(phase >= 180.0)
    phase -= 360.0;
  else if(phase < -180.0)
    phase += 360.0;

  return phase;
}

double restrict_degrees360(double phase){
  /**********************************************************************
  Input:  phase, in degrees but not always restricted to any domain.
  ***********************************************************************
  Output: phase, in degrees and restricted to [0,+360] domain.
  **********************************************************************/
  //First modulo phases with abs > 360.
  if(fabs(phase) >= 360.0)
    phase = fmod(phase,360.0);

  //Now restrict phases to 0..360.
  if(phase < 0.0)
    phase += 360.0;

  return phase;
}

double restrict_radians(double phase){
  /**********************************************************************
  Input:  phase, in radians but not always restricted to any domain.
  ***********************************************************************
  Output: phase, in radians and restricted to [-pi,+pi] domain.
  **********************************************************************/
  //First modulo phases with abs > 2pi.
  if(fabs(phase) >= twoPi)
    phase = fmod(phase,twoPi);

  //Now restrict phases to +-pi.
  if(phase >= pi)
    phase -= twoPi;
  else if(phase < -pi)
    phase += twoPi;

  return phase;
}

void convert_results_from_ampphase_to_sincos(results_s &results){
  /**********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k;
  double tempamp,tempphase;
  period_info_s period_info;

  //Assume there isn't any need for conversion unless proven otherwise.
  results.conversion_to_sincos=1;
  //Loop through output types (periods count twice)- only affect amp/phases.
  for(k=0;k<(long long)results.options.output_type.size();k++){
    //New output types should be added here.
    if(results.options.output_type.at(k)==101 or results.options.output_type.at(k)==102){
      //I'm assuming that if ANY sines,cosines are found, that there are NO
      //amps or phases.
      //So set "conversion_to_sincos" to 1, indicating that no conversion
      //took place.
      results.conversion_to_sincos=1;
      return;
    }
    else if(results.options.output_type.at(k)==104){//Phase detected.
      //I'm assuming that if ANY phases are found, there are NO sin/cos
      //So set "conversion_to_sincos" to 2, indicating that conversion
      //succeeded in changing results to sincos.
      results.conversion_to_sincos=2;
      results.options.output_type.at(k) = 102;//Change phase to sin coef.
      results.options.output_type.at(k-1) = 101;//Change amp to cos coef.
      //Amp and phase need to be converted back to cosamp/sinamp.
      //Only do this for output_types 104 (phase2sinamp) but convert the
      //"k-1" entry from amp to cos coef as well.  This needs to be
      //done simultaneously!
      //Loop through grid points to change amps/phases at all grid points.
      if(results.options.output_choice == 1){
        for(i=0;i<(long long)results.outputs.at(k).size();i++){
          tempamp = results.outputs.at(k-1).at(i);
          tempphase = results.outputs.at(k).at(i)*deg2rad;
          results.outputs.at(k-1).at(i) = tempamp*cos(tempphase);
          results.outputs.at(k).at(i) = tempamp*sin(tempphase);
        }//End of loop through grid points.
      }
      else if(results.options.output_choice == 5){
        for(j=0;j<(long long)results.latlon.lat.size();j++){
          for(i=0;i<(long long)results.latlon.lon.size();i++){
            //Only touch these values if they're not mask values.
            if(results.latlon.outputs.at(k-1).at(j).at(i) != results.latlon.mask and results.latlon.outputs.at(k).at(j).at(i) != results.latlon.mask){
              tempamp = results.latlon.outputs.at(k-1).at(j).at(i);
              tempphase = results.latlon.outputs.at(k).at(j).at(i)*deg2rad;
              results.latlon.outputs.at(k-1).at(j).at(i) = tempamp*cos(tempphase);
              results.latlon.outputs.at(k).at(j).at(i) = tempamp*sin(tempphase);
            }
          }
        }
      }
      else cout<<"WARNING!!! Conversion didn't happen because results.options.output_choice = "<<results.options.output_choice<<endl;
    }//End of "if this is a phase, convert it and previous to cosamp/sinamp.
  }//End of loop through output_type.

  //Just to make sure...
  if((long long)results.options.output_type.size() != (long long)results.titles.size()) cout<<"!!!!!!!!!WOAH!!! SHOULD *NEVER*, EVER, **EVER** SEE THIS!!!hf342"<<endl;
  //Change titles and units from amp/phase (cm,deg) to sin/cos (cm,cm).
  //Loop through output with k (periods count twice), but index param
  //vector w/ i, where periods are listed once.  Don't advance i when
  //cos coefficients (previously inaccurately referred to as cos amps) are
  //found.  This way sine amps will be on correct periods.
  i=0;

  for(k=0;k<(long long)results.titles.size();k++){
    //New output types should be added here.
    if(results.options.output_type.at(k)==102){//Sinamp detected
      is_this_period_recognized(results.options.parameter.at(i),period_info);

      results.titles.at(k-1) = period_info.title;
      results.titles.at(k-1).append(" Cosine Coefficient");
      results.titles.at(k) = period_info.title;
      results.titles.at(k).append(" Sine Coefficient");
      results.units.at(k) = results.units.at(k-1);//k-1 units were already correct.
      i++;
    }
    else if(results.options.output_type.at(k)!=101) i++;//Any but cos coefs..
  }
}

void convert_results_from_sincos_to_ampphase(results_s &results){
  /**********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k;
  period_info_s period_info;

  //Assume there isn't any need for conversion unless proven otherwise.
  results.conversion_to_ampphase=1;

  //Loop through output types (periods count twice)- only affect sin/cos.
  for(k=0;k<(long long)results.options.output_type.size();k++){
    //New output types should be added here.
    if(results.options.output_type.at(k)==103 or results.options.output_type.at(k)==104){
      //I'm assuming that if ANY amps/phases are found, that there are NO
      //sines or cosines.
      //So set "conversion_to_ampphase" to 1, indicating that no conversion
      //took place.
      results.conversion_to_ampphase=1;
      return;
    }
    else if(results.options.output_type.at(k)==102){//Sine coef detected.
      //I'm assuming that if ANY sines are found, that there are NO amp/phas
      //So set "conversion_to_ampphase" to 2, indicating that conversion
      //succeeded in changing results to ampphase.
      results.conversion_to_ampphase=2;
      results.options.output_type.at(k) = 104;//Change sine coef to phase.
      results.options.output_type.at(k-1) = 103;//Change cos coef to amp.
      //Cos/sin coefs need to be changed back to amp and phase.
      //Only do this for output_types 3 (sin2phase) but convert the
      //"k-1" entry from cosamp to amp as well.  This needs to be
      //done simultaneously!
      //Loop through grid points to change amps/phases at all grid points.
      if(results.options.output_choice == 1){
        for(i=0;i<(long long)results.outputs.at(k).size();i++){
          double cos_value,sin_value;
          cos_value = results.outputs.at(k-1).at(i);
          sin_value = results.outputs.at(k).at(i);
          results.outputs.at(k-1).at(i) = sqrt(pow(sin_value,2)+pow(cos_value,2));
          results.outputs.at(k).at(i) = rad2deg*atan2(sin_value,cos_value);
        }//End of loop through grid points.
      }
      else if(results.options.output_choice == 5){
        for(j=0;j<(long long)results.latlon.lat.size();j++){
          for(i=0;i<(long long)results.latlon.lon.size();i++){
            //Only touch these values if they're not mask values.
            if(results.latlon.outputs.at(k-1).at(j).at(i) != results.latlon.mask and results.latlon.outputs.at(k).at(j).at(i) != results.latlon.mask){
              double cos_value,sin_value;
              cos_value = results.latlon.outputs.at(k-1).at(j).at(i);
              sin_value = results.latlon.outputs.at(k).at(j).at(i);
              results.latlon.outputs.at(k-1).at(j).at(i) = sqrt(pow(sin_value,2)+pow(cos_value,2));
              results.latlon.outputs.at(k).at(j).at(i) = restrict_degrees360(rad2deg*atan2(sin_value,cos_value));
            }
          }
        }
      }
      else cout<<"WARNING!!! Conversion didn't happen because results.options.output_choice = "<<results.options.output_choice<<endl;
    }//End of "if this is a phase, convert it and previous to cosamp/sinamp.
  }//End of loop through output_type.

  //Just to make sure...
  if((long long)results.options.output_type.size() != (long long)results.titles.size()) cout<<"!!!!!!!!!WOAH!!! SHOULD *NEVER*, EVER, **EVER** SEE THIS!!!dsjw78rkjgfd"<<endl;
  //Change titles and units from sin/cos (cm,cm) to amp/phase (cm,deg).
  //Loop through output with k (periods count twice), but index param
  //vector w/ i, where periods are listed once.  Don't advance i when
  //cos coefficients (previously inaccurately referred to as cos amps) are
  //found.  This way sine coeffs will be on correct periods.
  i=0;
  for(k=0;k<(long long)results.titles.size();k++){
    //New output types should be added here.
    if(results.options.output_type.at(k)==104){//Phase detected
      is_this_period_recognized(results.options.parameter.at(i),period_info);

      results.titles.at(k-1) = period_info.title;
      results.titles.at(k-1).append(" Amplitude");
      results.titles.at(k) = period_info.title;
      results.titles.at(k).append(" Phase");
      results.units.at(k) = "degrees";
      i++;
    }
    else if(results.options.output_type.at(k)!=103) i++;//Any but amps..
  }
}

void convert_rawfes_to_latlon(results_s &results){
  /**********************************************************************
  Purpose: This function converts rawfes amps/phases (from disk) to 
            results output, with outputs and titles, units, parameters,
            parameter_types, output_types; all in latlon format.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k,l;
  latlon_s rawfes;
  model_s model;
  string file;
  model.options.type=1;//FES
  define_model_titles_and_periods(model);

  results.options.output_choice = 5;

  //Over-write original parameters with model periods.
  results.options.parameter.clear();
  results.options.parameter = model.periods;

  //output_type size holds official number of outputs.
  //They're all periods, so this is twice as long as the parameter vector.
  results.options.output_type.clear();
  results.options.output_type.resize(results.options.parameter.size()*2);

  //Don't init latlon.outputs yet because the rawfes latlon grid isn't loaded yet.

  //For the moment they're ALL tides.
  results.options.output_tide.clear();
  results.options.output_tide.resize(results.options.output_type.size(),1);

  results.options.parameter_type.clear();//They're all periods.
  results.options.parameter_type.resize(results.options.parameter.size(),101);
  //For the moment they're ALL tides.
  results.options.parameter_tide.clear();
  results.options.parameter_tide.resize(results.options.parameter.size(),1);

  results.titles.clear();
  results.units.clear();

  //Loop through model periods ("M2", "K1", etc)
  k=0;//accounts for amp/phase.
  for(l=0;l<(long long)model.titles.size();l++){
    //Open input file for loading.
    file = gaiafolder();
    file.append(model.options.basefolder);
    file.append(model.options.filenames.at(l));
    rawfes.filename = file;
    rawfes.discard_redundant_values = 0;
    read_rawfes(rawfes);

    if(l==0){//On 1st period, copy grid and init outputs multivector.
      results.latlon.lat = rawfes.lat;
      results.latlon.lon = rawfes.lon;
      results.latlon.mask = rawfes.mask;//Used to avoid converting mask values to/from sin/cos.
  
      //Initialize latlon output.
      results.latlon.outputs.clear();
      results.latlon.outputs.resize(results.options.output_type.size());
      for(i=0;i<(long long)results.latlon.outputs.size();i++){
        results.latlon.outputs.at(i).resize(results.latlon.lat.size());
        for(j=0;j<(long long)results.latlon.lat.size();j++){
          results.latlon.outputs.at(i).at(j).resize(results.latlon.lon.size());
        }
      }
      //Initialize latlon elevations, which are derived from the unstructured grid.
      results.latlon.elev.clear();
      results.latlon.elev.resize(results.latlon.lat.size());
      for(j=0;j<(long long)results.latlon.lat.size();j++){
        results.latlon.elev.at(j).resize(results.latlon.lon.size());
      }
    }
    
    //Set amp title.
    results.titles.push_back(model.titles.at(l));
    results.titles.at(k).append(" Amplitude");
    results.units.push_back("cm");

    //First, save amplitudes.
    //Loop through latlon latitudes.
    for(j=0;j<(long long)results.latlon.lat.size();j++){
      //Loop through latlon longitudes.
      for(i=0;i<(long long)results.latlon.lon.size();i++){
        results.latlon.outputs.at(k).at(j).at(i) = rawfes.amplitudes.at(j).at(i);
      }//End of loop through longitudes.
    }//End of loop through latitudes.

    //Note that this is an amplitude.
    results.options.output_type.at(k)=103;

    k++;//Now for phase.

    //Set phase title.
    results.titles.push_back(model.titles.at(l));
    results.titles.at(k).append(" Phase");
    results.units.push_back("degrees");

    //Next, save phases.
    //Loop through latlon latitudes.
    for(j=0;j<(long long)results.latlon.lat.size();j++){
      //Loop through latlon longitudes.
      for(i=0;i<(long long)results.latlon.lon.size();i++){
        results.latlon.outputs.at(k).at(j).at(i) = rawfes.phases.at(j).at(i);
      }//End of loop through longitudes.
    }//End of loop through latitudes.

    //Note that this is a phase.
    results.options.output_type.at(k)=104;

    k++;//Now for next amplitude.

  }//End of loop through periods.
}

void convert_latlon_to_rawfes(results_s &results){
  /**********************************************************************
  Purpose:  This function converts latlon output to rawfes amps/phases,
            then writes them to disk one at a time.
            Crude checks make sure that the periods in the latlon 
            output matches those in define_model_titles_and_periods(). 
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k,l;
  latlon_s rawfes;
  model_s model;
  period_info_s period_info;
  string file;

  model.options.type=1;//FES
  define_model_titles_and_periods(model);

  //Check that periods are the same for FES and the latlon output.
  for(l=0;l<(long long)model.periods.size();l++){
    convert_title_to_parameter(results.titles.at(l),period_info);
    if(fabs(model.periods.at(l)-period_info.period)>tolerance){
      cout<<"WARNING!!! Model period "<<model.periods.at(l)<<" doesn't match latlon period "<<period_info.period<<endl;
      return;
    }
  }

  //Convert latlon to amp/phase just in case it was in sin/cos format.
  convert_results_from_sincos_to_ampphase(results);

  //Copy grid.
  rawfes.lat = results.latlon.lat;
  rawfes.lon = results.latlon.lon;
  rawfes.mask = results.latlon.mask;

  //Loop through model periods ("M2", "K1", etc)
  k=0;//accounts for amp/phase.
  for(l=0;l<(long long)model.titles.size();l++){
    //Open input file for loading.
    file = gaiafolder();
    file.append(model.options.basefolder);
    file.append("new/");
    file.append(model.options.filenames.at(l));
    rawfes.filename = file;
    rawfes.discard_redundant_values = 0;//Is this always 0, or should it be variable?

    //Initialize rawfes multivectors.
    rawfes.amplitudes.clear();
    rawfes.phases.clear();
    rawfes.amplitudes.resize(rawfes.lat.size());
    rawfes.phases.resize(rawfes.lat.size());
    for(j=0;j<(long long)rawfes.lat.size();j++){
      rawfes.amplitudes.at(j).resize(rawfes.lon.size());
      rawfes.phases.at(j).resize(rawfes.lon.size());
    }

    //Loop through latlon latitudes.
    for(j=0;j<(long long)results.latlon.lat.size();j++){
      //Loop through latlon longitudes.
      for(i=0;i<(long long)results.latlon.lon.size();i++){
        rawfes.amplitudes.at(j).at(i) = results.latlon.outputs.at(k).at(j).at(i);
        rawfes.phases.at(j).at(i)     = results.latlon.outputs.at(k+1).at(j).at(i);
      }//End of loop through longitudes.
    }//End of loop through latitudes.
    write_rawfes(rawfes);
    k+=2;//Each period has amp and phase.
  }//End of loop through periods.
  
  //If needed, convert amp/phase back to sin/cos.
  if(results.conversion_to_ampphase==2) convert_results_from_ampphase_to_sincos(results);
}

void convert_model_to_results(grid_s &grid, results_s &results, model_s &model){
  /**********************************************************************
  Purpose:  This function converts model amps/phases to results
            output, with outputs and titles, units, parameters,
            parameter_types, output_types, etc.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k;

  //Over-write original parameters with model periods.
  results.options.parameter.clear();
  results.options.parameter = model.periods;

  //output_type size holds official number of outputs.
  //They're all periods, so this is twice as long as the parameter vector.
  results.options.output_type.clear();
  results.options.output_type.resize(results.options.parameter.size()*2);

  //Resize first dimension of outputs so that it
  //can hold all model periods (2 each- one for amp, 1 for phase).
  results.outputs.clear();
  results.outputs.resize(results.options.output_type.size());

  //Now resize the second dimension (have to do this multiple times)
  //so that the second dimension holds all grid points.
  for(k=0;k<(long long)results.outputs.size();k++){
    results.outputs.at(k).resize(grid.lat.size());
  }

  //For the moment they're ALL tides.
  results.options.output_tide.clear();
  results.options.output_tide.resize(results.options.output_type.size(),1);

  results.options.parameter_type.clear();//They're all periods.
  results.options.parameter_type.resize(results.options.parameter.size(),101);
  //For the moment they're ALL tides.
  results.options.parameter_tide.clear();
  results.options.parameter_tide.resize(results.options.parameter.size(),1);

  results.titles.clear();
  results.units.clear();

  //Loop through model periods ("M2", "K1", etc)
  k=0;//accounts for amp/phase.
  for(i=0;i<(long long)model.titles.size();i++){
    //Set amp title.
    results.titles.push_back(model.titles.at(i));
    results.titles.at(k).append(" Amplitude");
    results.units.push_back("cm");

    //First, save amplitudes.
    for(j=0;j<(long long)grid.lat.size();j++){
      results.outputs.at(k).at(j) = model.amplitudes.at(i).at(j);
    }//End of loop through grid points.

    //Note that this is an amplitude.
    results.options.output_type.at(k)=103;

    k++;//Now for phase.

    //Set phase title.
    results.titles.push_back(model.titles.at(i));
    results.titles.at(k).append(" Phase");
    results.units.push_back("degrees");

    //Next, save phases.
    for(j=0;j<(long long)grid.lat.size();j++){//Restrict to -180..180
      results.outputs.at(k).at(j) = restrict_degrees(model.phases.at(i).at(j));
    }//End of loop through grid points.

    //Note that this is a phase.
    results.options.output_type.at(k)=104;

    k++;//Now for next amplitude.

  }//End of loop through periods.
}

void convert_model_from_ampphase_to_sincos(model_s &model, grid_s &grid){
  /**********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;

  //Just to make sure...
  if((long long)model.periods.size() != (long long)model.amplitudes.size()) cout<<(long long)model.periods.size()<<" != "<<(long long)model.amplitudes.size()<<"! dfkeu74m"<<endl;
  if((long long)model.titles.size() != (long long)model.phases.size()) cout<<(long long)model.titles.size()<<" != "<<(long long)model.phases.size()<<"! djdv834m"<<endl;

  //1st dimension is the period.
  model.sin.clear(); model.cos.clear();
  model.sin.resize(model.periods.size());
  model.cos.resize(model.periods.size());

  //2nd dimension contains different grid points.
  for(i=0;i<(long long)model.periods.size();i++){
    model.sin.at(i).resize(grid.lat.size(),0.0);
    model.cos.at(i).resize(grid.lat.size(),0.0);
  }

  //Loop through periods.
  for(j=0;j<(long long)model.periods.size();j++){
    //Loop through grid points to change amps/phases at all grid points.
    for(i=0;i<(long long)grid.lat.size();i++){
      model.sin.at(j).at(i) = model.amplitudes.at(j).at(i)*sin(model.phases.at(j).at(i)*deg2rad);
      model.cos.at(j).at(i) = model.amplitudes.at(j).at(i)*cos(model.phases.at(j).at(i)*deg2rad);
    }//End of loop through grid points.
  }//End of loop through periods.
}

void convert_model_from_sincos_to_ampphase(model_s &model, grid_s &grid){
  /**********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;

  //Just to make sure...
  if((long long)model.periods.size() != (long long)model.sin.size() or (long long)model.titles.size() != (long long)model.cos.size()){
    cout<<"!!!!!!!!!WOAH!!! SHOULD *NEVER*, EVER, **EVER** SEE THIS!!!sdvklwe9"<<endl;
    cout<<"model.periods.size(): "<<(long long)model.periods.size()<<endl;
    cout<<"model.titles.size(): "<<(long long)model.titles.size()<<endl;
  }

  //1st dimension is the period.
  model.amplitudes.clear(); model.phases.clear();
  model.amplitudes.resize(model.periods.size());
  model.phases.resize(model.periods.size());

  //2nd dimension contains different grid points.
  for(i=0;i<(long long)model.periods.size();i++){
    model.amplitudes.at(i).resize(grid.lat.size(),0.0);
    model.phases.at(i).resize(grid.lat.size(),0.0);
  }

  //Loop through periods.
  for(j=0;j<(long long)model.periods.size();j++){
    //Loop through grid points to change amps/phases at all grid points.
    for(i=0;i<(long long)grid.lat.size();i++){
      model.amplitudes.at(j).at(i) = sqrt(pow(model.sin.at(j).at(i),2)+pow(model.cos.at(j).at(i),2));
      model.phases.at(j).at(i) = rad2deg*atan2(model.sin.at(j).at(i),model.cos.at(j).at(i));
    }//End of loop through grid points.
  }//End of loop through periods.
}

void create_folder(string &folder){
  /**********************************************************************
  Purpose:  This function creates a folder, which may contain spaces and
            is assumed (for the moment) to be an absolute path.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          string folder - Name of folder to be created, may contain spaces
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  FILE *output;
  string temp_string;

  //Create folder with desired foldername in the maps folder.
  temp_string = "mkdir \"";
  temp_string.append(folder);
  temp_string.append("\"");
  //cout<<"folder creation string: "<<temp_string<<endl;
  output = popen(temp_string.c_str(),"r");
  pclose(output);
}

void remove_folder(string &folder){
  /**********************************************************************
  Purpose:  This function removes a folder, which may contain spaces and
            is assumed (for the moment) to be an absolute path.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          string folder - Name of folder to be created, may contain spaces
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  FILE *output;
  string temp_string;

  //Create folder with desired foldername in the maps folder.
  temp_string = "rm -rf \"";
  temp_string.append(folder);
  temp_string.append("\"");
  //cout<<"folder removal string: "<<temp_string<<endl;
  output = popen(temp_string.c_str(),"r");
  pclose(output);
}

void create_output_folder(int argc, char *argv[],
                          plot_options_s &plot_options){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  char s[max_length];

  string temp_string;
  FILE *output;
  string temp_string2;

  string base;//This string forms the base part of the output filename.

  //The next variables are used to load data from cache or save to cache.
  string foldername, current_foldername,true_foldername,filename,newfile;

  timespan_conversion_s timespan_conversion;

  string prefix = current_datetime(); prefix.append(" - ");
  
  //If this program is called with any command line arguments, assume that
  //those arguments are the intended extension for the folder that will be
  //created.
  plot_options.outputfolder=gaiafolder();
  plot_options.outputfolder.append("output/maps/");
  string idl_foldername;
  if(argc > 1){
    temp_string=prefix;
    for(i=1;i<argc;i++){
      temp_string.append(argv[i]);
      if(i<argc-1) temp_string.append(" ");
    }
    plot_options.outputfolder.append(temp_string);
  }
  else{//If no command line args, omit the description but leave the time.
    plot_options.outputfolder.append(prefix);
  }
  idl_foldername = plot_options.outputfolder;
  plot_options.outputfolder.append("/");
  //cout<<"relative output folder: "<<temp_string<<endl;
  //cout<<"absolute output folder: "<<plot_options.outputfolder<<endl;

  //Create folder with desired foldername in the maps folder.
  create_folder(plot_options.outputfolder);

  //Look for cgaia_marker.txt, created by cXgaia.
  string marker_string(gaiafolder());
  marker_string.append("c/multiple_cgaia_marker.txt");
  FILE *markerfp;
  markerfp = fopen(marker_string.c_str(),"r");

  //If file doesn't exist, use old code.
  if(!markerfp){
    //Make copy_gaia_log.sh so after gaia, log is cp'd to output fldr
    temp_string2 = "echo 'cp log_gaia.txt \"";
    temp_string2.append("");
    temp_string2.append(plot_options.outputfolder);
    temp_string2.append("\"' > copy_gaia_log.sh ; chmod +x copy_gaia_log.sh");
    //cout<<"copy_gaia_log.sh creation and chmod string: "<<temp_string2<<endl;
    output = popen(temp_string2.c_str(),"r");
    pclose(output);

    //Make copy_idl_log.sh so after idl, log is cp'd to output fldr
    temp_string2 = "echo 'cp log_idl.txt \"";
    temp_string2.append("");
    temp_string2.append(plot_options.outputfolder);
    temp_string2.append("\"' > copy_idl_log.sh ; chmod +x copy_idl_log.sh");
    //cout<<"copy_idl_log.sh creation and chmod string: "<<temp_string2<<endl;
    output = popen(temp_string2.c_str(),"r");
    pclose(output);

    //Are plots requested?
    if(plot_options.noplots != 1){
      //Create run.pro so IDL runs create_plots.pro on output folder.
      temp_string2 = "cd ../IDL ; ";
      temp_string2.append("echo 'create_plots, \"");
      temp_string2.append(idl_foldername);
      temp_string2.append("\"' > run.pro ; ");
      temp_string2.append("echo 'exit' >> run.pro");
      //cout<<"run.pro creation string: "<<temp_string2<<endl;
      output = popen(temp_string2.c_str(),"r");
      pclose(output);

      //LATER PUT THIS CODE IN AN IF-THEN SO IT ONLY ACTIVATES WHEN
      //SAVE OVERLAP POINTS > 1 *AND* REGION CODE IS BEING USED *AND*
      //OVERLAP IS > 0. ALSO, IS THERE AN EASY WAY TO DISABLE FOLDER
      //CREATION IN THOSE CASES?
      //Create run.pro so IDL runs create_plots.pro on OVERLAP folder.
      temp_string2 = "cd ../IDL ; ";
      temp_string2.append("echo 'create_plots, \"");
      temp_string2.append(idl_foldername);
      temp_string2.append("/region_overlap_plots");
      temp_string2.append("\"' > run_overlap_maps.pro ; ");
      temp_string2.append("echo 'exit' >> run_overlap_maps.pro");
      //cout<<"run_overlap_maps.pro creation string: "<<temp_string2<<endl;
      output = popen(temp_string2.c_str(),"r");
      pclose(output);

    }
    else{
      //Create dummy run.pro so no plots are made.
      temp_string2 = "cd ../IDL ; ";
      temp_string2.append("echo 'exit' > run.pro");
      //cout<<"run.pro creation string: "<<temp_string2<<endl;
      output = popen(temp_string2.c_str(),"r");
      pclose(output);
    }
  }//End of "if cgaia_marker.txt not found"
  else{

    //Read marker number from file.
    int call_number;
    char* f_return;//Used to stop fgets return value warning.
    f_return=fgets(s,max_length,markerfp);
    sscanf(s,"%d", &call_number);

    //Close file, then delete it.
    fclose(markerfp);
    if(remove(marker_string.c_str()) != 0) cout<<"Problem deleting file "<<marker_string<<endl;

    if(call_number==2){
      //Rewrite as soon as possible.

      //Make copy_gaia_log2.sh so after gaia, log is cp'd to output fldr
      temp_string2 = "echo 'cp log_gaia2.txt \"";
      temp_string2.append("");
      temp_string2.append(plot_options.outputfolder);
      temp_string2.append("\"' > copy_gaia_log2.sh ; chmod +x copy_gaia_log2.sh");
      //cout<<"copy_gaia_log2.sh creation and chmod string: "<<temp_string2<<endl;
      output = popen(temp_string2.c_str(),"r");
      pclose(output);

      //Make copy_idl_log2.sh so after idl, log is cp'd to output fldr
      temp_string2 = "echo 'cp log_idl2.txt \"";
      temp_string2.append("");
      temp_string2.append(plot_options.outputfolder);
      temp_string2.append("\"' > copy_idl_log2.sh ; chmod +x copy_idl_log2.sh");
      //cout<<"copy_idl_log2.sh creation and chmod string: "<<temp_string2<<endl;
      output = popen(temp_string2.c_str(),"r");
      pclose(output);

      //Are plots requested?
      if(plot_options.noplots != 1){
        //Create run2.pro so IDL runs create_plots.pro on output folder.
        temp_string2 = "cd ../IDL ; ";
        temp_string2.append("echo 'create_plots, \"");
        temp_string2.append(idl_foldername);
        temp_string2.append("\"' > run2.pro ; ");
        temp_string2.append("echo 'exit' >> run2.pro");
        //cout<<"run2.pro creation string: "<<temp_string2<<endl;
        output = popen(temp_string2.c_str(),"r");
        pclose(output);
      }
      else{
        //Create dummy run.pro so no plots are made.
        temp_string2 = "cd ../IDL ; ";
        temp_string2.append("echo 'exit' > run2.pro");
        //cout<<"run2.pro creation string: "<<temp_string2<<endl;
        output = popen(temp_string2.c_str(),"r");
        pclose(output);
      }
    }//End of "if call_number==2."
    else if(call_number==3){
      //Rewrite as soon as possible.

      //Make copy_gaia_log3.sh so after gaia, log is cp'd to output fldr
      temp_string2 = "echo 'cp log_gaia3.txt \"";
      temp_string2.append("");
      temp_string2.append(plot_options.outputfolder);
      temp_string2.append("\"' > copy_gaia_log3.sh ; chmod +x copy_gaia_log3.sh");
      //cout<<"copy_gaia_log3.sh creation and chmod string: "<<temp_string2<<endl;
      output = popen(temp_string2.c_str(),"r");
      pclose(output);

      //Make copy_idl_log3.sh so after idl, log is cp'd to output fldr
      temp_string2 = "echo 'cp log_idl3.txt \"";
      temp_string2.append("");
      temp_string2.append(plot_options.outputfolder);
      temp_string2.append("\"' > copy_idl_log3.sh ; chmod +x copy_idl_log3.sh");
      //cout<<"copy_idl_log3.sh creation and chmod string: "<<temp_string2<<endl;
      output = popen(temp_string2.c_str(),"r");
      pclose(output);

      //Are plots requested?
      if(plot_options.noplots != 1){
        //Create run3.pro so IDL runs create_plots.pro on output folder.
        temp_string2 = "cd ../IDL ; ";
        temp_string2.append("echo 'create_plots, \"");
        temp_string2.append(idl_foldername);
        temp_string2.append("\"' > run3.pro ; ");
        temp_string2.append("echo 'exit' >> run3.pro");
        //cout<<"run3.pro creation string: "<<temp_string2<<endl;
        output = popen(temp_string2.c_str(),"r");
        pclose(output);
      }
      else{
        //Create dummy run.pro so no plots are made.
        temp_string2 = "cd ../IDL ; ";
        temp_string2.append("echo 'exit' > run3.pro");
        //cout<<"run3.pro creation string: "<<temp_string2<<endl;
        output = popen(temp_string2.c_str(),"r");
        pclose(output);
      }
    }//End of "if call_number==3."
    else cout<<"!!!WARNING!!! Unrecognized call_number: "<<call_number<<endl;

  }//End of "else if cgaia_marker.txt was found"
}

string gaiafolder(){
  /**********************************************************************
  Purpose: This function assumes that it's being run from
           GAIAFOLDER/c, so it reports the "realpath" of ".."
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)

  Future upgrades should consider:
  http://insanecoding.blogspot.com/2007/11/implementing-realpath-in-c.html
  http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
  **********************************************************************/
  char resolved_path[PATH_MAX];
  char* f_return;//Used to stop fgets return value warning.
  f_return = realpath("..", resolved_path);
  string output = resolved_path;
  output.append("/");//Folders always end in "/".
  return output;
}

void load_model(model_s &model, grid_s &grid){
  /**********************************************************************
  Purpose: This function loads a tide model such as FES2004, GOT00.2, etc.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i=0,j,k,l,counter=0;
  string basefolder(gaiafolder());
  string file;
  double temp,temp2;

  //These variables are used to search the cache folder for matching grids.
  DIR *cacheDIR;
  struct dirent *cacheDirEnt;
  //These variables are used to load a matching grid from cache.
  FILE *in_fp;
  //These variables are used to store a new cache file.
  //string newfile = grid_options.cachefolder;
  string filename;//stores JUST the filename of cache file (not the path).
  char s[max_length];

  char* f_return;//Used to stop fgets return value warning.

  //-1 is a signal to revert to the above value after grid is defined so
  //if a_o.type=0 and the loaded grid is different than specified here,
  //no problem occurs.
  if(model.options.halfwidth == -1){
    model.options.halfwidth=grid.options.lat_spacing/2;
    cout<<"Autoselecting model halfwidth of "<<model.options.halfwidth<<" km."<<endl;
  }

  //Ditto.
  if(model.options.averaging_radius == -1){
    model.options.averaging_radius=2*model.options.halfwidth;
    cout<<"Autoselecting model averaging radius of "<<model.options.averaging_radius<<" km."<<endl;
  }

  //Initialize variables for notification function for time remaining.
  notification_s notification,blank_notif;
  //1st notification occurs after two loops, but it usually won't print
  //anything because not enough total_time will have passed (first_interval).
  //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
  notification.interval=2;
  //Desired min and max intervals in seconds b/w notifications.
  notification.first_interval=10;
  notification.min_interval=300, notification.max_interval=900;
  //notification.min_interval=30, notification.max_interval=90;
  notification.start_time=-1; notification.current_time=-1;
  notification.previous_time=-1;//Needs to be initialized
  notification.prefix = "Model-to-grid interpolation";

  notification.starting_index = 0;
  notification.partial_saves_enabled = 0;

  //Used to size notification intervals.
  notification.previous_i=notification.starting_index;
  notification.num_loops = (long long)grid.lat.size()-notification.starting_index;
  //notification.interval will not be allowed to go above this number
  //so that at LEAST a certain number of messages are seen.
  notification.max_notif_interval=(long long)(notification.num_loops/4);

  //For some reason only the first file printed notifs. notification
  //will be overwritten with blank_notif every loop to try to stop this.
  blank_notif=notification;

  //Annoyingly, these need to be defined! Argh!
  least_squares_s dummy_ls;
  input_s dummy_input;
  results_s dummy_ar;

  //Search for previously loaded models in the cache, but only if
  //noload == 0 and this grid isn't custom.
  if(model.options.noload == 0 and grid.options.type !=0){
    cout<<"Searching cache for preloaded model..."<<endl;
    //Check to see if the cache contains a file with the same options
    //as the ones loaded in model.options.  If so, set i=1.
    i=0;//Init as 0 because it hasn't found matching grid in cache yet.

    //First, figure out exactly what filename we're looking for by
    //creating a filename that corresponds to currently selected options.
    filename=create_model_cache_filename(grid, model);

    //Open the cache folder.
    //cout<<filename<<endl;
    //cout<<model.options.cachefolder<<endl;
    cacheDIR = opendir(model.options.cachefolder.c_str());

    //Examine each file, see if it contains the desired grid.
    cacheDirEnt = readdir(cacheDIR);
    while(cacheDirEnt != NULL && i == 0){
      //If this filename matches desired grid options, set i=1 to exit loop.
      if(strcmp(filename.c_str(),cacheDirEnt->d_name)==0) i = 1;
      cacheDirEnt = readdir(cacheDIR);
    }

    // Release the open folder.
    closedir(cacheDIR);

    //If matching file was found, load it and skip rest of function.
    if(i == 1){
      printf("Loading model from cache...\n");
      file = model.options.cachefolder;
      file.append(filename);
      in_fp = fopen(file.c_str(),"r");
      //Read the title.
      f_return=fgets(s,max_length,in_fp);
      model.title = s;
      //Remove the \n at the end of the line.
      if((k=model.title.find("\n"))!=(int)string::npos){
        model.title.swap(model.title.erase(k,2));
      }

      //Read the number of model periods ("M2", "K1", etc)
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &l);
      cout<<"Number of model periods: "<<l<<endl;
      cout<<"grid.lat.size(): "<<grid.lat.size()<<endl;
      //Based on the number of periods just read, resize vectors.
      model.titles.resize(l);
      model.periods.resize(l);
      model.omegas.resize(l);
      //1st dimension contains different tidal periods "M2", "K1", etc.
      model.amplitudes.resize(l);
      model.phases.resize(l);
      //2nd dimension contains different grid points.
      for(l=0;l<(int)model.periods.size();l++){
        model.amplitudes.at(l).resize(grid.lat.size());
        model.phases.at(l).resize(grid.lat.size());
      }
      //Loop through all periods, loading title, filename, period and values.
      for(l=0;l<(int)model.periods.size();l++){
        //Read the title.
        f_return=fgets(s,max_length,in_fp);
        model.titles.at(l) = s;
        //Remove the \n at the end of the line.
        if((k=model.titles.at(l).find("\n"))!=(int)string::npos){
          model.titles.at(l).swap(model.titles.at(l).erase(k,2));
        }

        //Read the period.
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf", &temp);
        model.periods.at(l) = temp;

        //Read the angular frequency.
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf", &temp);
        model.omegas.at(l) = temp;

        //Skip the next line, which is just a header.
        f_return=fgets(s,max_length,in_fp);
        //Now loop through all grid pts, reading amp and phase.
        for(k=0;k<(long long)grid.lat.size();k++){
          f_return=fgets(s,max_length,in_fp);
          sscanf(s,"%lf %lf", &temp, &temp2);//Temp vars.
          model.amplitudes.at(l).at(k) = temp;
          model.phases.at(l).at(k) = temp2;
        }
      }
      fclose(in_fp);
      printf("Finished loading model.\n");

      //Skip the loading code using a (gasp!) goto function.
      return;
    }//End of "if matching file was found" if statement.
    else cout <<"No matching cache file found.  Loading model from scratch..."<<endl;
  }//End of "if noload == 0" if statement.

  switch(model.options.type){
    default: cout<<"!!!!WARNING!!!!!! model.options.type "<<model.options.type<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 0: return;//Skip model loading.
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{//FES2004 amps/phases at gr pts using land pts too, and is_pt_in_bin.

      latlon_s rawfes;
      double current_angle;//Still used in record_nearby_values.

      define_model_titles_and_periods(model);

      //"basefolder" contains the entire path.
      basefolder.append(model.options.basefolder);

      //Used to over-write model and grid at end with actual FES values/coords
      //around SINGLE grid point in grid if record_nearby_values == 1
      model_s temp_model;
      grid_s temp_grid, temp_grid2;//2nd is used for elevations only.
      //Copy these now that they've been defined.
      temp_model.periods = model.periods;
      temp_model.titles = model.titles;
      //1st dimension is the period.
      temp_model.amplitudes.resize(temp_model.periods.size());
      temp_model.phases.resize(temp_model.periods.size());

      //Now that I know how many different tidal periods will be defined,
      //set the sizes of the amplitudes and phases vectors of vectors.
      //No!  Set up the sin and cos vectors of vectors because that's how
      //the averaging will be done.  Amp,phase will be created by function
      //convert_model_from_sincos_to_ampphase()
      //1st dimension contains different tidal periods "M2", "K1", etc.
      model.sin.resize(model.periods.size());
      model.cos.resize(model.periods.size());

      //2nd dimension contains different grid points.
      for(l=0;l<(int)model.periods.size();l++){
        model.sin.at(l).resize(grid.lat.size(),0.0);
        model.cos.at(l).resize(grid.lat.size(),0.0);
      }

      //If averaging radius lies along N-S line, how many deg of lat is it?
      double lat_difference = (rad2deg*model.options.averaging_radius)/(planet_avg_radius);

      //Cycle through all filenames, load them one at a time.
      for(l=0;l<(int)model.options.filenames.size();l++){
        //Open input file for loading.
        file = basefolder;
        file.append(model.options.filenames.at(l));
        rawfes.filename = file;
        rawfes.discard_redundant_values = 1;
        read_rawfes(rawfes);

        if(model.options.record_nearby_values == 1){
          long long grid_point_count=0;

          //Just checking... there should only be a single station here!
          if(grid.lat.size() != 1) cout<<"!!!WARNING grid.lat.size() = "<<grid.lat.size()<<" and it should always be 1 here!"<<endl;

          //Loop through FES latitudes.
          for(j=0;j<(long long)rawfes.lat.size();j++){
            //Don't bother continuing unless this line of lat is close to
            //grid.lat[k] - where close is determined by lat_difference.
            if(fabs(rawfes.lat.at(j)-grid.lat.front()) <= lat_difference){
              //Loop through FES longitudes.
              for(i=0;i<(long long)rawfes.lon.size();i++){
                current_angle = spherical_distance(rawfes.lat.at(j),rawfes.lon.at(i),grid.lat.front(),grid.lon.front());

                //Only add this point to temp_grid/model if it's close enough.
                if(current_angle*planet_avg_radius <= model.options.averaging_radius){
                  //Still need to add the point but zero any mask values.
                  if(rawfes.amplitudes.at(j).at(i) != rawfes.mask){
if(l==1) cout<<"Made it. l = "<<l<<", amp.size() = "<<temp_model.amplitudes.size()<<endl;
                    temp_model.amplitudes.at(l).push_back(rawfes.amplitudes.at(j).at(i));
                    temp_model.phases.at(l).push_back(rawfes.phases.at(j).at(i));
                  }
                  else{
                    temp_model.amplitudes.at(l).push_back(0);
                    temp_model.phases.at(l).push_back(0);
                  }

                  //First period has to fill in grid for first time.
                  if(l==0){
                    temp_grid.lat.push_back(rawfes.lat.at(j));
                    temp_grid.lon.push_back(rawfes.lon.at(i));
                    temp_grid.elev.push_back(0);//(temp_grid2.elev.front());
                  }
                  //Next loops just check coords.
                  else{
                    if(temp_grid.lat.at(grid_point_count) != rawfes.lat.at(j)) cout<<"!!!WARNING at l="<<l<<" temp_grid.lat.at("<<grid_point_count<<") = "<<temp_grid.lat.at(grid_point_count)<<" while lat.at("<<j<<") = "<<(long long)rawfes.lat.at(j)<<endl;
                    if(temp_grid.lon.at(grid_point_count) != rawfes.lon.at(i)) cout<<"!!!WARNING at l="<<l<<" temp_grid.lon.at("<<grid_point_count<<") = "<<temp_grid.lon.at(grid_point_count)<<" while lon.at("<<i<<") = "<<(long long)rawfes.lat.at(i)<<endl;
                    grid_point_count++;
                  }
                }//End of "this FES point is close enough".
              }//End of loop through FES lons.
            }//End of "if this line of lat is close enough" statement.
          }//End of loop through FES lats.

          //Only necessary the very first time.
          if(l==0){
            cout<<"Found "<<temp_grid.lat.size()<<" FES points around this station."<<endl;
          }
          //But next loops do check it.
          else{
            if((long long)temp_grid.lat.size() != grid_point_count) cout<<"!!!WARNING temp_grid.lat.size() = "<<temp_grid.lat.size()<<" which != grid_point_count ("<<grid_point_count<<")."<<endl;
          }
        }//end of record_nearby_values.
        else{//Only bother doing this if NOT recording nearby values, b/c it CRASHES.
     
          cout<<"Finished reading model data.  Now converting to grid coordinates..."<<endl;

          //This needs to be re-initialized every time.
          notification=blank_notif;
          notification.start_time = time(NULL);

          //Loop through main grid points, average amps/phases in each bin.
          for(k=0;k<(long long)grid.lat.size();k++){
            //Just so bin boundaries are defined. lat,lon indices NEED to be zero!
            int newbin=1; is_pt_in_bin(grid,rawfes.lat.front(),rawfes.lon.front(),k,newbin);

            long numpoints=0;//Number of FES pts in this grid pt's bin.
            notify(notification,k,dummy_ls,dummy_input,dummy_ar);
            //Loop through FES latitudes.
            for(j=0;j<(long long)rawfes.lat.size();j++){
              //Don't bother continuing unless this lat is in the bin's lat limits.
              if(rawfes.lat.at(j)<grid.maxlat and rawfes.lat.at(j)>=grid.minlat){
                //Loop through FES longitudes.
                for(i=0;i<(long long)rawfes.lon.size();i++){
                  //Only add this point to temp_grid/model if it's in the bin
                  //if(fabs(grid.lat.at(k)-61.59)<0.1 or fabs(grid.lat.at(k)-71.7)<0.1) cout<<"Mascon "<<setw(6)<<k<<", lat "<<setw(6)<<fixed<<setprecision(2)<<grid.lat.at(k)<<", lon "<<setw(6)<<fixed<<setprecision(2)<<grid.lon.at(k)<<", FES lat "<<setw(6)<<fixed<<setprecision(2)<<rawfes.lat.at(j)<<", FES lon "<<setw(6)<<fixed<<setprecision(2)<<rawfes.lon.at(i)<<", sin: "<<setw(14)<<scientific<<setprecision(7)<<rawfes.amplitudes.at(j).at(i)*sin(rawfes.phases.at(j).at(i)*deg2rad)<<", is_pt_in_bin: "<<is_pt_in_bin(grid,rawfes.lat.at(j),rawfes.lon.at(i),k,newbin)<<endl;
                  if(is_pt_in_bin(grid,rawfes.lat.at(j),rawfes.lon.at(i),k,newbin)){
                    numpoints++;
                    //Add the point but zero any mask values.
                    if(rawfes.amplitudes.at(j).at(i) != rawfes.mask){
                      model.sin.at(l).at(k) += rawfes.amplitudes.at(j).at(i)*sin(rawfes.phases.at(j).at(i)*deg2rad);
                      model.cos.at(l).at(k) += rawfes.amplitudes.at(j).at(i)*cos(rawfes.phases.at(j).at(i)*deg2rad);
                    }
                  }//End of "if this FES pt is in the bin."
                }//End of loop through FES lons.
              }//End of "if this line of lat is in bin's lat limits."
            }//End of loop through FES lats.

            //Now that all closest orig pts have been summed, divide by
            //number of points to obtain the true weighted avg.
            model.sin.at(l).at(k) /= (double)numpoints;
            model.cos.at(l).at(k) /= (double)numpoints;

          }//End of loop through main grid points.
        }//End of "if not set to record nearby values"
      }//End of loop through model.options.filenames.

      cout<<"Finished interpolating model data. Now converting from sin/cos to amp/phase."<<endl;
      convert_model_from_sincos_to_ampphase(model,grid);

      //Now, at very end, model and grid are overwritten if desired.
      if(model.options.record_nearby_values == 1){
        model = temp_model;
        model.options.record_nearby_values = 1;//This was just over-written!
        grid = temp_grid;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 2:{//Improved FES loading code using spherical_distance as 2nd pass.

      latlon_s rawfes;
      double current_angle;//Still used in record_nearby_values.
      long long enough_warnings=50;//Otherwise 2nd pass prints too many warnings.

      define_model_titles_and_periods(model);

      //"basefolder" contains the entire path.
      basefolder.append(model.options.basefolder);

      //Used to over-write model and grid at end with actual FES values/coords
      //around SINGLE grid point in grid if record_nearby_values == 1
      model_s temp_model;
      grid_s temp_grid, temp_grid2;//2nd is used for elevations only.
      //Copy these now that they've been defined.
      temp_model.periods = model.periods;
      temp_model.titles = model.titles;
      //1st dimension is the period.
      temp_model.amplitudes.resize(temp_model.periods.size());
      temp_model.phases.resize(temp_model.periods.size());

      //Now that I know how many different tidal periods will be defined,
      //set the sizes of the amplitudes and phases vectors of vectors.
      //No!  Set up the sin and cos vectors of vectors because that's how
      //the averaging will be done.  Amp,phase will be created by function
      //convert_model_from_sincos_to_ampphase()
      //1st dimension contains different tidal periods "M2", "K1", etc.
      model.sin.resize(model.periods.size());
      model.cos.resize(model.periods.size());

      //2nd dimension contains different grid points.
      for(l=0;l<(int)model.periods.size();l++){
        model.sin.at(l).resize(grid.lat.size(),0.0);
        model.cos.at(l).resize(grid.lat.size(),0.0);
      }

      //If averaging radius lies along N-S line, how many deg of lat is it?
      double lat_difference = (rad2deg*model.options.averaging_radius)/(planet_avg_radius);

      //Cycle through all filenames, load them one at a time.
      for(l=0;l<(int)model.options.filenames.size();l++){
        //Open input file for loading.
        file = basefolder;
        file.append(model.options.filenames.at(l));
        rawfes.filename = file;
        rawfes.discard_redundant_values = 1;
        read_rawfes(rawfes);
        
        if(l==0){
          long long missed_bins=0;//Keeps track of how many pts weren't binned.
          long long doubled_bins=0;//Keeps track of how many pts binned > once.
          int verbose=1;
          grid.fes_index.resize(rawfes.lat.size());
          for(j=0;j<(long long)rawfes.lat.size();j++) grid.fes_index.at(j).resize(rawfes.lon.size());

          grid.fes_lat_indices.resize(grid.lat.size());
          grid.fes_lon_indices.resize(grid.lat.size());
          //Loop through main grid points.
          for(k=0;k<(long long)grid.lat.size();k++){
            //Just so bin boundaries are defined. lat,lon indices NEED to be zero!
            int newbin=1; is_pt_in_bin(grid,rawfes.lat.front(),rawfes.lon.front(),k,newbin);
            //Loop through FES latitudes.
            for(j=0;j<(long long)rawfes.lat.size();j++){
              //Don't bother continuing unless this lat is in the bin's lat limits.
              if(rawfes.lat.at(j)<grid.maxlat and rawfes.lat.at(j)>=grid.minlat){
                //Loop through FES longitudes.
                for(i=0;i<(long long)rawfes.lon.size();i++){
                  if(is_pt_in_bin(grid,rawfes.lat.at(j),rawfes.lon.at(i),k,newbin)){
                    grid.fes_index.at(j).at(i).push_back(k);
                  }//End of "if this FES pt is in the bin."
                }//End of loop through FES lons.
              }//End of "if this line of lat is in bin's lat limits."
            }//End of loop through FES lats.
          }//End of loop through main grid points.
          //Now that I've binned the "easy" points, it's time to catch the
          //few points that fall on the line between bins, so they're not
          //caught by "is_pt_in_bin".  For these, use spherical_dist().
          //Loop through FES latitudes.
          for(j=0;j<(long long)rawfes.lat.size();j++){
            //Loop through FES longitudes.
            for(i=0;i<(long long)rawfes.lon.size();i++){
              //Only examine FES points that are in the grid boundaries.
              if(is_pt_in_grid(grid,rawfes.lat.at(j),rawfes.lon.at(i)) == 1){
                //Finalize FES points that were binned exactly once.
                if(grid.fes_index.at(j).at(i).size() == 1){
                  long long templl = grid.fes_index.at(j).at(i).front();
                  grid.fes_lat_indices.at(templl).push_back(j);
                  grid.fes_lon_indices.at(templl).push_back(i);
                }
                //Examine FES points that haven't been binned.
                else if(grid.fes_index.at(j).at(i).size() == 0){
                  if(verbose and missed_bins < enough_warnings) cout<<"FES pt at "<<setw(10)<<fixed<<setprecision(4)<<rawfes.lat.at(j)<<", "<<rawfes.lon.at(i)<<" was chosen by "<<grid.fes_index.at(j).at(i).size()<<" bins using fast code. "<<endl;
                  long long templl = grid_index_via_distance(grid, rawfes.lat.at(j),rawfes.lon.at(i), verbose and missed_bins < enough_warnings);
                  grid.fes_index.at(j).at(i).push_back(templl);
                  grid.fes_lat_indices.at(templl).push_back(j);
                  grid.fes_lon_indices.at(templl).push_back(i);
                  missed_bins++;
                }
                //Or, if this point was placed in >1 bin, find the closer one.
                else if(grid.fes_index.at(j).at(i).size() > 1){
                  if(verbose and doubled_bins < enough_warnings) cout<<"FES pt at "<<setw(10)<<fixed<<setprecision(4)<<rawfes.lat.at(j)<<", "<<rawfes.lon.at(i)<<" was chosen by "<<grid.fes_index.at(j).at(i).size()<<" bins using fast code. "<<endl;
                  long long templl = grid_index_via_distance(grid, rawfes.lat.at(j),rawfes.lon.at(i), verbose and doubled_bins < enough_warnings);
                  if(verbose and doubled_bins < enough_warnings and count(grid.fes_index.at(j).at(i).begin(),grid.fes_index.at(j).at(i).end(),templl) != 1){
                    cout<<"NOTE: Bins picked by fast code are at:"<<endl;
                    for(long long temper=0;temper<(long long)grid.fes_index.at(j).at(i).size();temper++) cout<<"Lat: "<<grid.lat.at(grid.fes_index.at(j).at(i).at(temper))<<", "<<grid.lon.at(grid.fes_index.at(j).at(i).at(temper))<<endl;
                  }
                  grid.fes_index.at(j).at(i).clear();
                  grid.fes_index.at(j).at(i).push_back(templl);
                  grid.fes_lat_indices.at(templl).push_back(j);
                  grid.fes_lon_indices.at(templl).push_back(i);
                  doubled_bins++;
                }
              }
            }
          }
          cout<<"Number of FES points that weren't binned the first time: "<<missed_bins<<endl;
          cout<<"Number of FES points that were binned twice (or more) the first time: "<<doubled_bins<<endl;
        }

        if(model.options.record_nearby_values == 1){
          long long grid_point_count=0;

          //Just checking... there should only be a single station here!
          if(grid.lat.size() != 1) cout<<"!!!WARNING grid.lat.size() = "<<grid.lat.size()<<" and it should always be 1 here!"<<endl;

          //Loop through FES latitudes.
          for(j=0;j<(long long)rawfes.lat.size();j++){
            //Don't bother continuing unless this line of lat is close to
            //grid.lat[k] - where close is determined by lat_difference.
            if(fabs(rawfes.lat.at(j)-grid.lat.front()) <= lat_difference){
              //Loop through FES longitudes.
              for(i=0;i<(long long)rawfes.lon.size();i++){
                current_angle = spherical_distance(rawfes.lat.at(j),rawfes.lon.at(i),grid.lat.front(),grid.lon.front());

                //Only add this point to temp_grid/model if it's close enough.
                if(current_angle*planet_avg_radius <= model.options.averaging_radius){
                  //Still need to add the point but zero any mask values.
                  if(rawfes.amplitudes.at(j).at(i) != rawfes.mask){
if(l==1) cout<<"Made it. l = "<<l<<", amp.size() = "<<temp_model.amplitudes.size()<<endl;
                    temp_model.amplitudes.at(l).push_back(rawfes.amplitudes.at(j).at(i));
                    temp_model.phases.at(l).push_back(rawfes.phases.at(j).at(i));
                  }
                  else{
                    temp_model.amplitudes.at(l).push_back(0);
                    temp_model.phases.at(l).push_back(0);
                  }

                  //First period has to fill in grid for first time.
                  if(l==0){
                    temp_grid.lat.push_back(rawfes.lat.at(j));
                    temp_grid.lon.push_back(rawfes.lon.at(i));
                    temp_grid.elev.push_back(0);//(temp_grid2.elev.front());
                  }
                  //Next loops just check coords.
                  else{
                    if(temp_grid.lat.at(grid_point_count) != rawfes.lat.at(j)) cout<<"!!!WARNING at l="<<l<<" temp_grid.lat.at("<<grid_point_count<<") = "<<temp_grid.lat.at(grid_point_count)<<" while lat.at("<<j<<") = "<<(long long)rawfes.lat.at(j)<<endl;
                    if(temp_grid.lon.at(grid_point_count) != rawfes.lon.at(i)) cout<<"!!!WARNING at l="<<l<<" temp_grid.lon.at("<<grid_point_count<<") = "<<temp_grid.lon.at(grid_point_count)<<" while lon.at("<<i<<") = "<<(long long)rawfes.lat.at(i)<<endl;
                    grid_point_count++;
                  }
                }//End of "this FES point is close enough".
              }//End of loop through FES lons.
            }//End of "if this line of lat is close enough" statement.
          }//End of loop through FES lats.

          //Only necessary the very first time.
          if(l==0){
            cout<<"Found "<<temp_grid.lat.size()<<" FES points around this station."<<endl;
          }
          //But next loops do check it.
          else{
            if((long long)temp_grid.lat.size() != grid_point_count) cout<<"!!!WARNING temp_grid.lat.size() = "<<temp_grid.lat.size()<<" which != grid_point_count ("<<grid_point_count<<")."<<endl;
          }
        }//end of record_nearby_values.
        else{//Only bother doing this if NOT recording nearby values, b/c it CRASHES.
     
          cout<<"Finished reading model data.  Now converting to grid coordinates..."<<endl;

          //This needs to be re-initialized every time.
          notification=blank_notif;
          notification.start_time = time(NULL);

          //Loop through main grid points, average amps/phases in each bin.
          for(k=0;k<(long long)grid.lat.size();k++){

            notify(notification,k,dummy_ls,dummy_input,dummy_ar);
            //Loop through FES pts in this bin.
            if(grid.fes_lat_indices.at(k).size() != grid.fes_lon_indices.at(k).size()) cout<<"WARNING!!! fes_lat/lon_indices have different sizes!!!"<<endl;
            for(i=0;i<(long long)grid.fes_lat_indices.at(k).size();i++){
              long long tempj = grid.fes_lat_indices.at(k).at(i);
              long long tempi = grid.fes_lon_indices.at(k).at(i);
              //Add the point but zero any mask values.
              if(rawfes.amplitudes.at(tempj).at(tempi) != rawfes.mask){
                model.sin.at(l).at(k) += rawfes.amplitudes.at(tempj).at(tempi)*sin(rawfes.phases.at(tempj).at(tempi)*deg2rad);
                model.cos.at(l).at(k) += rawfes.amplitudes.at(tempj).at(tempi)*cos(rawfes.phases.at(tempj).at(tempi)*deg2rad);
              }
            }//End of loop through FES pts in this mascon.

            //Now that all closest orig pts have been summed, divide by
            //number of points to obtain the true weighted avg.
            model.sin.at(l).at(k) /= (double)grid.fes_lat_indices.at(k).size();
            model.cos.at(l).at(k) /= (double)grid.fes_lat_indices.at(k).size();

          }//End of loop through main grid points.
        }//End of "if not set to record nearby values"
      }//End of loop through model.options.filenames.

      cout<<"Finished interpolating model data. Now converting from sin/cos to amp/phase."<<endl;
      convert_model_from_sincos_to_ampphase(model,grid);

      //Now, at very end, model and grid are overwritten if desired.
      if(model.options.record_nearby_values == 1){
        model = temp_model;
        model.options.record_nearby_values = 1;//This was just over-written!
        grid = temp_grid;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 3:{//Improved FES loading code weighted using FES pts' areas.

      latlon_s rawfes;
      double current_angle;//Still used in record_nearby_values.

      define_model_titles_and_periods(model);

      //"basefolder" contains the entire path.
      basefolder.append(model.options.basefolder);

      //Used to over-write model and grid at end with actual FES values/coords
      //around SINGLE grid point in grid if record_nearby_values == 1
      model_s temp_model;
      grid_s temp_grid, temp_grid2;//2nd is used for elevations only.
      //Copy these now that they've been defined.
      temp_model.periods = model.periods;
      temp_model.titles = model.titles;
      //1st dimension is the period.
      temp_model.amplitudes.resize(temp_model.periods.size());
      temp_model.phases.resize(temp_model.periods.size());

      //Now that I know how many different tidal periods will be defined,
      //set the sizes of the amplitudes and phases vectors of vectors.
      //No!  Set up the sin and cos vectors of vectors because that's how
      //the averaging will be done.  Amp,phase will be created by function
      //convert_model_from_sincos_to_ampphase()
      //1st dimension contains different tidal periods "M2", "K1", etc.
      model.sin.resize(model.periods.size());
      model.cos.resize(model.periods.size());

      //2nd dimension contains different grid points.
      for(l=0;l<(int)model.periods.size();l++){
        model.sin.at(l).resize(grid.lat.size(),0.0);
        model.cos.at(l).resize(grid.lat.size(),0.0);
      }

      //If averaging radius lies along N-S line, how many deg of lat is it?
      double lat_difference = (rad2deg*model.options.averaging_radius)/(planet_avg_radius);

      //Cycle through all filenames, load them one at a time.
      for(l=0;l<(int)model.options.filenames.size();l++){
        //Open input file for loading.
        file = basefolder;
        file.append(model.options.filenames.at(l));
        rawfes.filename = file;
        rawfes.discard_redundant_values = 1;
        read_rawfes(rawfes);
        
        if(l==0){
          match_fes_to_grid(rawfes,grid);
        }

        if(model.options.record_nearby_values == 1){
          long long grid_point_count=0;

          //Just checking... there should only be a single station here!
          if(grid.lat.size() != 1) cout<<"!!!WARNING grid.lat.size() = "<<grid.lat.size()<<" and it should always be 1 here!"<<endl;

          //Loop through FES latitudes.
          for(j=0;j<(long long)rawfes.lat.size();j++){
            //Don't bother continuing unless this line of lat is close to
            //grid.lat[k] - where close is determined by lat_difference.
            if(fabs(rawfes.lat.at(j)-grid.lat.front()) <= lat_difference){
              //Loop through FES longitudes.
              for(i=0;i<(long long)rawfes.lon.size();i++){
                current_angle = spherical_distance(rawfes.lat.at(j),rawfes.lon.at(i),grid.lat.front(),grid.lon.front());

                //Only add this point to temp_grid/model if it's close enough.
                if(current_angle*planet_avg_radius <= model.options.averaging_radius){
                  //Still need to add the point but zero any mask values.
                  if(rawfes.amplitudes.at(j).at(i) != rawfes.mask){
if(l==1) cout<<"Made it. l = "<<l<<", amp.size() = "<<temp_model.amplitudes.size()<<endl;
                    temp_model.amplitudes.at(l).push_back(rawfes.amplitudes.at(j).at(i));
                    temp_model.phases.at(l).push_back(rawfes.phases.at(j).at(i));
                  }
                  else{
                    temp_model.amplitudes.at(l).push_back(0);
                    temp_model.phases.at(l).push_back(0);
                  }

                  //First period has to fill in grid for first time.
                  if(l==0){
                    temp_grid.lat.push_back(rawfes.lat.at(j));
                    temp_grid.lon.push_back(rawfes.lon.at(i));
                    temp_grid.elev.push_back(0);//(temp_grid2.elev.front());
                  }
                  //Next loops just check coords.
                  else{
                    if(temp_grid.lat.at(grid_point_count) != rawfes.lat.at(j)) cout<<"!!!WARNING at l="<<l<<" temp_grid.lat.at("<<grid_point_count<<") = "<<temp_grid.lat.at(grid_point_count)<<" while lat.at("<<j<<") = "<<(long long)rawfes.lat.at(j)<<endl;
                    if(temp_grid.lon.at(grid_point_count) != rawfes.lon.at(i)) cout<<"!!!WARNING at l="<<l<<" temp_grid.lon.at("<<grid_point_count<<") = "<<temp_grid.lon.at(grid_point_count)<<" while lon.at("<<i<<") = "<<(long long)rawfes.lat.at(i)<<endl;
                    grid_point_count++;
                  }
                }//End of "this FES point is close enough".
              }//End of loop through FES lons.
            }//End of "if this line of lat is close enough" statement.
          }//End of loop through FES lats.

          //Only necessary the very first time.
          if(l==0){
            cout<<"Found "<<temp_grid.lat.size()<<" FES points around this station."<<endl;
          }
          //But next loops do check it.
          else{
            if((long long)temp_grid.lat.size() != grid_point_count) cout<<"!!!WARNING temp_grid.lat.size() = "<<temp_grid.lat.size()<<" which != grid_point_count ("<<grid_point_count<<")."<<endl;
          }
        }//end of record_nearby_values.
        else{//Only bother doing this if NOT recording nearby values, b/c it CRASHES.
     
          cout<<"Finished reading model data.  Now converting to grid coordinates..."<<endl;

          //This needs to be re-initialized every time.
          notification=blank_notif;
          notification.start_time = time(NULL);

          //Loop through main grid points, average amps/phases in each bin.
          for(k=0;k<(long long)grid.lat.size();k++){

            notify(notification,k,dummy_ls,dummy_input,dummy_ar);
            //Loop through FES pts in this bin.
            double denom=0.0;
            if(grid.fes_lat_indices.at(k).size() == 0 or grid.fes_lon_indices.at(k).size() == 0) cout<<"WARNING!!! fes_lat/lon_indices are empty!!!"<<endl;
            if(grid.fes_lat_indices.at(k).size() != grid.fes_lon_indices.at(k).size()) cout<<"WARNING!!! fes_lat/lon_indices have different sizes!!!"<<endl;
            for(i=0;i<(long long)grid.fes_lat_indices.at(k).size();i++){
              long long tempj = grid.fes_lat_indices.at(k).at(i);
              long long tempi = grid.fes_lon_indices.at(k).at(i);
              denom += grid.fes_areas.at(tempj);
              //Add the point but zero any mask values.
              if(rawfes.amplitudes.at(tempj).at(tempi) != rawfes.mask){
                model.sin.at(l).at(k) += rawfes.amplitudes.at(tempj).at(tempi)*sin(rawfes.phases.at(tempj).at(tempi)*deg2rad)*grid.fes_areas.at(tempj);
                model.cos.at(l).at(k) += rawfes.amplitudes.at(tempj).at(tempi)*cos(rawfes.phases.at(tempj).at(tempi)*deg2rad)*grid.fes_areas.at(tempj);
              }
            }//End of loop through FES pts in this mascon.

            //Now that all closest orig pts have been summed, divide by
            //summed area of all points to obtain the true weighted avg.
            model.sin.at(l).at(k) /= denom;
            model.cos.at(l).at(k) /= denom;
            //cout<<"Mascon area: "<<setw(11)<<scientific<<setprecision(4)<<grid.area.at(k)<<", FES area: "<<denom<<", FES/mascon: "<<denom/grid.area.at(k)<<endl;
          }//End of loop through main grid points.
        }//End of "if not set to record nearby values"
      }//End of loop through model.options.filenames.

      cout<<"Finished interpolating model data. Now converting from sin/cos to amp/phase."<<endl;
      convert_model_from_sincos_to_ampphase(model,grid);

      //Now, at very end, model and grid are overwritten if desired.
      if(model.options.record_nearby_values == 1){
        model = temp_model;
        model.options.record_nearby_values = 1;//This was just over-written!
        grid = temp_grid;
      }

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10:{//AOTIM-5 using gaussian interp, amps/phases to main grid pts.

      double temp_double_array[18];//Used to load data from text file.
      model_s padman; //Holds high density Padman data.  Clear at end!
      vector<long long> closest_indices;
      vector<double> closest_angles;
      double current_angle;

      define_model_titles_and_periods(model);
      //"basefolder" contains the entire path.
      basefolder.append(model.options.basefolder);

      //Now that I know how many different tidal periods will be defined,
      //set the sizes of the amplitudes and phases vectors of vectors.
      //No!  Set up the sin and cos vectors of vectors because that's how
      //the averaging will be done.  Amp,phase will be created by function
      //convert_model_from_sincos_to_ampphase()
      //1st dimension contains different tidal periods "M2", "K1", etc.
      model.sin.resize(model.periods.size());
      model.cos.resize(model.periods.size());

      //2nd dimension contains different grid points.
      for(l=0;l<(int)model.periods.size();l++){
        model.sin.at(l).resize(grid.lat.size(),0.0);
        model.cos.at(l).resize(grid.lat.size(),0.0);
      }

      //Those were the amp and phase vectors of vectors that will show up
      //in the output.  Also need to initialize vectors of vectors for the
      //data in its original (denser) grid format.
      padman.amplitudes.resize(model.periods.size());
      padman.phases.resize(model.periods.size());

      //Open the single input file for loading.
      file = basefolder;
      file.append(model.options.filenames.front());
      in_fp = fopen(file.c_str(),"r");

      //Warn if file doesn't open correctly.
      if(in_fp == NULL) cout << "The model output file, " << file
                              << ", failed to open."<<endl;
      else cout << "Loading model output data from file "
                << file << "..."<<endl;

      //Ignore header line.
      f_return=fgets(s,max_length,in_fp);

      //Read amp and phase for all 8 periods at all (DENSE!) grid points.
      while(fgets(s,max_length,in_fp)!=NULL){
        sscanf(s,"%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",&temp_double_array[0],&temp_double_array[1],&temp_double_array[2],&temp_double_array[3],&temp_double_array[4],&temp_double_array[5],&temp_double_array[6],&temp_double_array[7],&temp_double_array[8],&temp_double_array[9],&temp_double_array[10],&temp_double_array[11],&temp_double_array[12],&temp_double_array[13],&temp_double_array[14],&temp_double_array[15],&temp_double_array[16],&temp_double_array[17]);
        padman.grid.lat.push_back(temp_double_array[0]);
        padman.grid.lon.push_back(temp_double_array[1]);
        padman.amplitudes.front().push_back(temp_double_array[2]);
        padman.phases.front().push_back(temp_double_array[3]);
        padman.amplitudes.at(1).push_back(temp_double_array[4]);
        padman.phases.at(1).push_back(temp_double_array[5]);
        padman.amplitudes.at(2).push_back(temp_double_array[6]);
        padman.phases.at(2).push_back(temp_double_array[7]);
        padman.amplitudes.at(3).push_back(temp_double_array[8]);
        padman.phases.at(3).push_back(temp_double_array[9]);
        padman.amplitudes.at(4).push_back(temp_double_array[10]);
        padman.phases.at(4).push_back(temp_double_array[11]);
        padman.amplitudes.at(5).push_back(temp_double_array[12]);
        padman.phases.at(5).push_back(temp_double_array[13]);
        padman.amplitudes.at(6).push_back(temp_double_array[14]);
        padman.phases.at(6).push_back(temp_double_array[15]);
        padman.amplitudes.at(7).push_back(temp_double_array[16]);
        padman.phases.at(7).push_back(temp_double_array[17]);
      }
      padman.titles = model.titles;
      padman.periods = model.periods;

      cout<<"Finished reading model data.  Now converting from amp/phase to sin/cos."<<endl;
      convert_model_from_ampphase_to_sincos(padman,padman.grid);

      //How many orig model pts are used to interpolate for final grid pt.
      //Calc this by specifying orig spacing in km bw padman gr pts, then
      //specifying how far away a pt can be and still be averaged.
      double orig_spacing=5;
      long long num_interp_pts=(long long)((pi*model.options.averaging_radius*model.options.averaging_radius)/(orig_spacing*orig_spacing));
      //If averaging radius lies along N-S line, how many deg of lat is it?
      double lat_difference = (rad2deg*model.options.averaging_radius)/(planet_avg_radius);

      if(grid.options.type==0) cout<<"Interpolating model with original spacing of "<<orig_spacing<<" km to custom grid.  Using Gaussian smoothing with halfwidth "<<model.options.halfwidth<<" km over an averaging region (per grid point) of radius "<<model.options.averaging_radius<<" km, extending "<<lat_difference<<" degrees to the N and S, that contains (approximately) "<<num_interp_pts<<" original model points."<<endl;
      else cout<<"Interpolating model with original spacing of "<<orig_spacing<<" km to grid with spacing of "<<grid.options.lat_spacing<<" km.  Using Gaussian smoothing with halfwidth "<<model.options.halfwidth<<" km over an averaging region (per grid point) of radius "<<model.options.averaging_radius<<" km, extending "<<lat_difference<<" degrees to the N and S, that contains (approximately) "<<num_interp_pts<<" original model points."<<endl;

      //Loop through (main) gr pts, find nearest model pointS for each one.
      for(k=0;k<(long long)grid.lat.size();k++){
        if((k+1) % 100 == 0) cout<<"Interpolating for pt "<<k+1<<" of "<<grid.lat.size()<<".  lat = "<<grid.lat.at(k)<<", lon = "<<grid.lon.at(k)<<endl;

        //Only bother to interpolate for this main pt if it's near ocean,
        //where "near" is defined by the averaging radius.
        int interpolate_me=0;
        //Interpolate ALL grid points in custom grid, cause they're all
        //tide gauges and I shouldn't miss any of them.
        if(grid.options.type == 0) interpolate_me=1;
        else if(grid.distance_to_ocean.at(k)<model.options.averaging_radius) interpolate_me=1;
        if(interpolate_me == 1){

          //Need to reset these vectors.
          closest_indices.clear();
          closest_indices.resize(num_interp_pts,-1);
          closest_angles.clear();
          //Next value is chosen because larger angles are "too far away".
          closest_angles.resize(num_interp_pts,lat_difference*deg2rad);

          //Loop through Padman's original dense grid.
          for(j=0;j<(long long)padman.grid.lat.size();j++){

            //cout<<"lat diff: "<<fabs(padman.grid.lat.at(j)-grid.lat.at(k))<<endl;
            //cout<<"main grid.lat_halfwidth["<<k<<"]: "<<grid.lat_halfwidth.at(k)<<endl;
            //cout<<"m2 amp at padman.grid["<<j<<"]: "<<padman.amplitudes.front().at(j)<<endl;
            //cout<<"padman grid size: "<<padman.grid.lat.size()<<endl;
            //cout<<"main grid size: "<<grid.lat.size()<<endl;
            //Only bother with distnce calc if this grid pt's lat is "close"
            //and if it's in the ocean, which means the M2 amp is not 0.
            if(fabs(padman.grid.lat.at(j) - grid.lat.at(k)) <= lat_difference and padman.amplitudes.front().at(j) != 0.0){
              current_angle = spherical_distance(padman.grid.lat.at(j), padman.grid.lon.at(j), grid.lat.at(k), grid.lon.at(k));

              //Check to see if this orig pt is closer to main grid pt
              //than the farthest "closest pt".
              l=num_interp_pts-1;
              if(current_angle < closest_angles.at(l)){
                closest_indices.at(l) = j;
                closest_angles.at(l) = current_angle;

                //Loop through closest_angles vector to see if this orig
                //pt's even closer than the farthest "closest pt".  If so,
                //keep swapping the angles and indices until there's a pt
                //that's closer, or the closest pt is reached.
                while(l>=1){
                  if(closest_angles.at(l)<closest_angles.at(l-1)){
                    swap_long(&closest_indices.at(l-1),&closest_indices.at(l));
                    swap_double(&closest_angles.at(l-1),&closest_angles.at(l));
                    l--;
                  }
                  else l=0;
                }
              }//End of "if orig gr pt's closer to main pt than farthest".
            }//End of "if orig lat is in halfwidth".
          }//End of loop through orig dense gr pts.

          //Now interpolate based on closest_angles vector, which now
          //holds the distances from the main grid point to the closest
          //original model pts.
          temp=0.0;//Holds the weighting factors.

          //First check to see that at least 1 pt was found "close enough".
          //If not, don't interpolate because this main grid point might
          //be outside the area of Padman's AOTIM-5 model. (val preset to 0)
          int interpolate=0;
          for(l=0;l<num_interp_pts;l++){
            //Convert from radians to arclength (km).
            closest_angles.at(l) *=planet_avg_radius;
            if(closest_indices.at(l) >= 0 and closest_angles.at(l)<=model.options.averaging_radius){
              //cout<<"Interpolating for pt "<<k+1<<" of "<<grid.lat.size()<<".  lat = "<<grid.lat.at(k)<<", lon = "<<grid.lon.at(k)<<endl;
              //cout <<"WARNING!! closest_indices["<<l<<"] for main grid point "<<k+1<<" is "<<closest_indices.at(l)<<endl;
              interpolate++;
            }
          }//End of loop to check whether or not "enough" points were found.

          if(interpolate>0){
            //cout<<"num_interp_pts: "<<num_interp_pts<<", num filled:  "<<interpolate<<endl;
            for(l=0;l<num_interp_pts;l++){
              //Allow for the possibility that not all closest pts positions
              //are filled.
              if(closest_indices.at(l) >= 0){
                if(closest_angles.at(l)>model.options.averaging_radius){
                  //cout<<"Interpolating for pt "<<k+1<<" of "<<grid.lat.size()<<".  lat = "<<grid.lat.at(k)<<", lon = "<<grid.lon.at(k)<<endl;
                  //cout <<"WARNING!! closest distance["<<l<<"] for main grid point "<<k+1<<" is "<<closest_angles.at(l)<<" km when averaging_radius = "<<model.options.averaging_radius<<" km."<<endl;
                  //cout<<"main grid elev = "<<grid.elev.at(k)<<endl;
                  //cout<<"Orig lat is "<<padman.grid.lat.at(closest_indices.at(l))<<" and lon is "<<padman.grid.lon.at(closest_indices.at(l))<<endl;
                }
                else{
                  double weightfactor;
                  //Add weighting factor for this orig grid point to sum.
                  weightfactor=exp(-pow(closest_angles.at(l),2)/(2*pow(model.options.halfwidth,2)));
                  temp+=weightfactor;
                  //Loop through model periods and interp all of em.
                  for(counter=0;counter<(long long)model.periods.size();counter++){
                    if(padman.amplitudes.at(counter).at(closest_indices.at(l)) == 0.0) cout <<"WARNING!! Amplitude "<<model.titles.at(counter)<<" at orig grid point with lat "<<padman.grid.lat.at(closest_indices.at(l))<<" and lon "<<padman.grid.lon.at(closest_indices.at(l))<<" is 0!"<<endl;
                    //if(counter==0) cout<<"M2 orig amp: "<<padman.amplitudes.at(counter).at(closest_indices.at(l))<<endl;
                    model.sin.at(counter).at(k) += padman.sin.at(counter).at(closest_indices.at(l))*weightfactor;
                    model.cos.at(counter).at(k) += padman.cos.at(counter).at(closest_indices.at(l))*weightfactor;
                  }//End of loop through model periods.
                }//End of "if distance is lte to averaging radius" else.
              }//End of "if close ind >= 0".
            }//End of loop through points to interpolate.

            //Now that all closest orig points have been summed, divide by
            //the weighting factors sum to obtain the true weighted average.
            for(counter=0;counter<(long long)model.periods.size();counter++){
              model.sin.at(counter).at(k) /= temp;
              //if(counter==0) cout<<"M2 final amp: "<<model.amplitudes.at(counter).at(k)<<endl;
              model.cos.at(counter).at(k) /= temp;
            }//End of loop through model periods.
          }//End of "interpolate" statements.
        }//End of "if main grid point is near ocean".
      }//End of loop through main grid points.

      cout<<"Finished interpolating model data.  Now converting from sin/cos to amp/phase."<<endl;
      convert_model_from_sincos_to_ampphase(model,grid);

      //Need to clear the high density data because it's HUGE.
      padman.amplitudes.clear();
      padman.phases.clear();
      padman.sin.clear();
      padman.cos.clear();
      padman.grid.lat.clear();
      padman.grid.lon.clear();

      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 101:{//FES minus AOTIM-5

      results_s temp_options;

      //model1 is FES2004, model2 is AOTIM-5.
      model_s model1,model2;

      model1.options = model.options; model2.options = model.options;

      //model1.options.noload = 0;//Purely for speed here, no other reason.
      //model2.options.noload = 0;//Purely for speed here, no other reason.

      cout<<"Current model type 101 displays FES2004 minus AOTIM-5."<<endl;

      cout<<"Loading FES2004 as first model."<<endl;
      model1.options.type = 1;
      load_model(model1, grid);
      cout<<"Loading AOTIM-5 as second model- the one to be subtracted."<<endl;
      model2.options.type = 2;
      load_model(model2, grid);

      //Copy stuff from AOTIM-5 model to the new model.
      model.periods = model2.periods;
      model.omegas = model2.omegas;

      //Set new model title and modify individual plot titles from AOTIM-5.
      model.title = "FES2004 - ";
      model.titles.clear();
      model.titles.resize(model2.titles.size());
      for(i=0;i<(long long)model.periods.size();i++){
        //cout<<"model2.titles.at(i): "<<model2.titles.at(i)<<endl;
        model.titles.at(i) = model.title;
        model.titles.at(i).append(model2.titles.at(i));
      }
      model.title.append(model2.title);

      //Set type and resize vectors.
      model.options.type = 101;
      //1st dimension is the period.
      model.sin.clear(); model.cos.clear();
      model.sin.resize(model.periods.size());
      model.cos.resize(model.periods.size());
      //2nd dimension contains different grid points.
      for(i=0;i<(long long)model.periods.size();i++){
        model.sin.at(i).resize(grid.lat.size(),0.0);
        model.cos.at(i).resize(grid.lat.size(),0.0);
      }

      //So that subtraction is simple.
      convert_model_from_ampphase_to_sincos(model1,grid);
      convert_model_from_ampphase_to_sincos(model2,grid);

      //Loop through periods.
      for(counter=0;counter<(long long)model.periods.size();counter++){

        //I'm assuming that FES2004 frequencies are a superset of
        //AOTIM-5 frequencies.  The period in AOTIM-5 at index counter is
        //the same as that in FES2004 at fescounter.
        long long fescounter=-1;
        for(l=0;l<(long long)model1.periods.size();l++){
          if(fabs(model1.periods.at(l) - model.periods.at(counter)) <= tolerance) fescounter = l;
        }

        //Loop through (main) gr pts, subtract the two models's sin and cos.
        for(k=0;k<(long long)grid.lat.size();k++){
          //Only bother to subtract models if this is an ocean point where
          //AOTIM-5 is defined (because FES covers all of AOTIM-5 and more).
          if(grid.elev.at(k)<0 and (model2.sin.at(counter).at(k) > 0.0 or model2.cos.at(counter).at(k) > 0.0)){
            model.sin.at(counter).at(k) = model1.sin.at(fescounter).at(k) - model2.sin.at(counter).at(k);
            model.cos.at(counter).at(k) = model1.cos.at(fescounter).at(k) - model2.cos.at(counter).at(k);
          }//End of "if main grid point is in ocean".
        }//End of loop through main grid points.
      }//End of periods loop.

      //Because output format is always(?) amplitude/phase.
      convert_model_from_sincos_to_ampphase(model,grid);

      break;
    }
  }//End switch-case

  //Save model in cache, but only if this isn't a custom grid or intended to
  //record nearby values.
  if(grid.options.type != 0 and model.options.record_nearby_values != 1){
    cout<<"Saving model to cache..."<<endl;
    filename=create_model_cache_filename(grid, model);
    file = model.options.cachefolder;
    file.append(filename);
    in_fp = fopen(file.c_str(),"w");
    fprintf(in_fp,"%s\n",model.title.c_str());
    fprintf(in_fp,"%d\n",(int)model.periods.size());
    for(l=0;l<(int)model.periods.size();l++){
      fprintf(in_fp,"%s\n",model.titles.at(l).c_str());
      //Dunno why I had this in there in the first place...
      //fprintf(in_fp,"%s\n",model.options.filenames.at(l).c_str());
      fprintf(in_fp,"%15.10f\n",model.periods.at(l));
      fprintf(in_fp,"%15.10f\n",model.omegas.at(l));
      fprintf(in_fp,"      Amplitude          Phase\n");
      for(i=0;i<(long long)grid.lat.size();i++){
        fprintf(in_fp,"%15.10f%15.10f\n",model.amplitudes.at(l).at(i),model.phases.at(l).at(i));
      }
    }
    fclose(in_fp);
    cout<<"Finished."<<endl;
  }
}

void insitu_comparison_verification(insitu_s &insitu,
                                    results_s &results,
                                    grid_s &grid,
                                    model_options_s &model_options,
                                    plot_options_s &plot_options){
  /**********************************************************************
  Purpose:  This function loads in situ data such as tide gauges and
            compares that data to either the model or the input data
            in many different ways.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k;

  char s[max_length];

  //Plotting all FES amps/phases within X km of each station.
  if(1==2){
    insitu.tg.grid.lat.clear();
    insitu.tg.grid.lon.clear();
    insitu.tg.titles.clear();
    insitu.tg.grid.lat.push_back(59);
    insitu.tg.grid.lon.push_back(292);
    insitu.tg.titles.push_back("Hudson strait");//*/
    double temp_lat_spacing=1000;
    cout<<"Plotting all FES amps/phases within "<<temp_lat_spacing<<" km of each station."<<endl;
    plot_options_s temp_plot_options, station_plot_options;
    results_s temp_results;
    model_s temp_model,blank_model;
    string base, temp_title;

    temp_plot_options = plot_options;//For the output folder name.
    temp_results.options.output_choice = results.options.output_choice;//Not in plot_options any more.    
    station_plot_options = plot_options;//For the output folder name.
    station_plot_options.just_the_filenames.clear();//Just making sure...

    grid_s temp_grid,blank_grid;

    //Loop through stations out here because each station returns its own
    //grid and "output".
    for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){

      //temp_grid should only have i'th station now.
      temp_grid=blank_grid;
      temp_grid.options.lat_spacing = temp_lat_spacing;
      temp_grid.options.type=0;//so dist_to_ocean isn't needed.
      temp_grid.distance_to_ocean.push_back(0);//What the heck...
      temp_grid.lat.push_back(insitu.tg.grid.lat.at(i));
      temp_grid.lon.push_back(insitu.tg.grid.lon.at(i));

      //Reset all this because load_model probably changes them.
      temp_model=blank_model;
      temp_model.options = model_options;//For the foldernames.
      temp_model.options.halfwidth = -1;//Use temp_grid.options.lat_spacing.
      temp_model.options.averaging_radius = -1;//Ditto.
      temp_model.options.type = 1;//FES 2004
      temp_model.options.record_nearby_values = 1;

      //Obtain nearby amps and phases at this station.
      load_model(temp_model, temp_grid);

      //record_nearby_values filled temp_model with amps,phases,periods,titles
      //temp_grid only has lat,lon,elev.

      //Prepend station name to titles here.
      sprintf(s,"station%04lld",i+1);
      base = s;
      for(j=0;j<(long long)temp_model.titles.size();j++){
        temp_title = temp_model.titles.at(j);
        temp_model.titles.at(j) = insitu.tg.titles.at(i);
        temp_model.titles.at(j).append(" ");
        temp_model.titles.at(j).append(temp_title);
      }

      convert_model_to_results(temp_grid,temp_results,temp_model);
      convert_results_from_ampphase_to_sincos(temp_results);

      //Write this station's files to disk.
      write_output(base, temp_results, temp_grid, temp_plot_options, 0);//0 because this isn't the final write_output.

      //Save the filenames for output_filenames (after station loop).
      for(j=0;j<(long long)temp_plot_options.just_the_filenames.size();j++){
        station_plot_options.just_the_filenames.push_back(temp_plot_options.just_the_filenames.at(j));
      }
    }

    //Create output_filenames with ALL those filenames.
    create_output_filenames(station_plot_options,temp_results);

  }//End of plots of model amps/phases around stations.

  //AutoFES plots
  if(1==2){
    cout<<"Plotting autoFES at each grid point..."<<endl;
    plot_options_s temp_plot_options;
    results_s temp_results, individual_results;
    model_s temp_model,blank_model,throwaway_model;
    insitu_s temp_insitu, template_insitu;
    string base, temp_title;
    time_conversion_s time_conversion;

    temp_plot_options = plot_options;//For the output folder name.
    base = "autoFES";

    //Create synth time series.
    template_insitu.synth_interval = 3600;//Between data points, in seconds.
    template_insitu.num_synth_points = 24*365*1;
    //Bizarrely, extra (long long) is needed when offset = 200*365*24*3600
    //otherwise:
    //gaia.cpp: In function ‘int insitu_comparison_verification(insitu_s&, ancillary_s&, results_s&, grid_s&, model_options_s&, input_s&, plot_options_s&)’:
    //gaia.cpp:1074: warning: integer overflow in expression
    template_insitu.synth_offset=0;//In seconds from J2000.
    create_synth_timeseries(template_insitu);

    //Define model periods so they can over-write temp_results.
    throwaway_model.options.type=1;//FES
    define_model_titles_and_periods(throwaway_model);
    temp_results.options.parameter = throwaway_model.periods;
    temp_results.options.parameter_type.resize(temp_results.options.parameter.size(),101);
    temp_results.options.sincos = 0;
    temp_results.options.output_choice = 1;//unstructured map.
    input_s dummy_input;//Moved above create_output_vectors() for arbitrary params.
    create_output_vectors(temp_results,dummy_input);

    //Resize the second dimension (have to do this multiple times)
    //so that the second dimension holds all grid points.
    for(i=0;i<(long long)temp_results.options.output_type.size();i++){
      temp_results.outputs.at(i).resize(grid.lat.size(),0.0);
    }
    //Initialize variables for notification function for time remaining.
    notification_s notification;
    //1st notification occurs after two loops, but it usually won't print
    //anything because not enough total_time will have passed (first_interval).
    //NEEDS TO BE 2 RATHER THAN 1; BAD THINGS HAPPEN WHEN IT'S SET TO 1.
    notification.interval=2;
    //Desired min and max intervals in seconds b/w notifications.
    notification.first_interval=10;
    notification.min_interval=600, notification.max_interval=900;
    notification.start_time=-1; notification.current_time=-1;
    notification.previous_time=-1;//Needs to be initialized
    notification.prefix = "AutoFES map";
    notification.starting_index = 0;
    notification.partial_saves_enabled = 0;
    //Used to size notification intervals.
    notification.previous_i=notification.starting_index;
    notification.num_loops = (long long)grid.lat.size()-notification.starting_index;
    //notification.interval will not be allowed to go above this number
    //so that at LEAST a certain number of messages are seen.
    notification.max_notif_interval=(long long)(notification.num_loops/4);

    //Annoyingly, these need to be defined! Argh!
    least_squares_s dummy_ls;
    //input_s dummy_input;Moved above create_output_vectors() for arbitrary params.
    results_s dummy_ar;

    notification.start_time = time(NULL);
    //Loop through the main grid.
    for(i=0;i<(long long)grid.lat.size();i++){

      notify(notification,i,dummy_ls,dummy_input,dummy_ar);

      //So that temp_insitu isn't different in subsequent loops.
      temp_insitu = template_insitu;
      //Clear and resize sealevel vector just in case.
      temp_insitu.tg.model.sealevels.clear();
      temp_insitu.tg.model.sealevels.resize(1);

      //temp_insitu grid should only have i'th pt now.
      temp_insitu.tg.grid=grid;//Blank slate- or rather normal slate.
      temp_insitu.tg.grid.options.type=0;//so dist_to_ocean !needed
      temp_insitu.tg.grid.distance_to_ocean.clear();
      temp_insitu.tg.grid.distance_to_ocean.push_back(0);//Why not?
      temp_insitu.tg.grid.lat.clear();
      temp_insitu.tg.grid.lon.clear();

      temp_insitu.tg.grid.lat.push_back(grid.lat.at(i));
      temp_insitu.tg.grid.lon.push_back(grid.lon.at(i));
      temp_insitu.tg.titles.resize(1);

      //Reset all this because generate_model_timeseries might change them.
      temp_model=blank_model;
      temp_model.options = model_options;//For the foldernames.
      //2 - load into fes_data one at a time, verbosely - debug!
      //3 - load all stations at once.
      temp_model.options.tg_type=3;//type 3 is faster but broken!

      generate_model_timeseries(temp_insitu,temp_model.options);
      //Needs to be in this variable for analyze_timeseries().
      temp_insitu.series1 = temp_insitu.tg.model.sealevels;

      if(i==0){
        if(temp_insitu.tg.model.periods != temp_results.options.parameter){
          cout<<"!!!WARNING MODEL.PERIODS != AR.OPT.PARAMETER!!!"<<endl;
        }
      }
      //Now that autoFES timeseries exists for this grid point, solve for
      //various amps and phases.
      individual_results = analyze_timeseries(temp_insitu);
      //Copy those amps and phases to temp_results structure.
      for(j=0;j<(long long)individual_results.outputs.size();j++){
        temp_results.outputs.at(j).at(i) = individual_results.outputs.at(j).front();
      }
    }//End of loop through grid.

    //Loop through model periods ("M2", "K1", etc) and record titles/units.
    k=0;//accounts for amp/phase.
    for(i=0;i<(long long)temp_insitu.tg.model.titles.size();i++){
      //Set amp title.
      temp_results.titles.push_back("auto");
      temp_results.titles.at(k).append(temp_insitu.tg.model.titles.at(i));
      temp_results.titles.at(k).append(" Amplitude");
      temp_results.units.push_back("cm");

      k++;//Now for phase.

      //Set phase title.
      temp_results.titles.push_back("auto");
      temp_results.titles.at(k).append(temp_insitu.tg.model.titles.at(i));
      temp_results.titles.at(k).append(" Phase");
      temp_results.units.push_back("degrees");

      k++;//Now for next amplitude.

    }//End of loop through periods.
    //Write the autoFES files to disk.
    write_output(base, temp_results, grid, temp_plot_options, 1);//1 because this is the final write_output.
  }//End of autoFES maps.

  int timeseries_plots_desired=1;

  //If requested, perform a comparison of the model to the insitu data.
  switch(insitu.comparison_type){
    default: cout<<"!!!!WARNING!!!!!! insitu.comparison_type "<<insitu.comparison_type<<" isn't recognized."<<endl;
    case 0: break;//No comparison at all.
    /////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////
    case 1:{//Compare autoFES to TGs.

      model_options_s insitu_model_options=model_options;
      //2 - load into fes_data one at a time, verbosely - debug!
      //3 - load all stations at once.
      insitu_model_options.tg_type=2;//type 3 is faster but broken!
      generate_model_timeseries(insitu,insitu_model_options);

      insitu.name1 = "in situ";
      insitu.name2 = "autoFES";
      insitu.series1 = insitu.tg.sealevels;
      insitu.series2 = insitu.tg.model.sealevels;

      //If timeseries_plots_desired, make plots of time series at each station.
      if(timeseries_plots_desired==1) make_timeseries_plots(insitu);

      compare_station_timeseries(insitu);

      break;
    }
    /////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////
    case 2:{//Compare real data to "TGs - autoFES."

      //Generate sealevel predictions using FES at all tide gauge stations.
      model_options_s insitu_model_options=model_options;
      //2 - load into fes_data one at a time, verbosely - debug!
      //3 - load all stations at once.
      insitu_model_options.tg_type=2;//type 3 is faster but broken!
      generate_model_timeseries(insitu,insitu_model_options);

      //Use the amplitudes and phases in results to generate a
      //sealevel signal at each tide gauge station at each time.
      //This requires matching the input.epoch time (which is in GPS time)
      //to the tide gauge times (which are in UTC time).
      //It also requires that insitu_initialization() found the closest
      //grid points to each station along with their respective weighting
      //factors to be used when "interpolating" the residual mascon signal
      //to the tide gauge station's location.
      generate_input_timeseries(insitu,results,grid,1);

      insitu.name1 = "insitu-FES";
      insitu.name2 = "real data";
      insitu.series1 = insitu.tg.sealevels;
      insitu.series2 = insitu.tg.input_sealevels;

      //series1 needs the autoFES model subtracted from it.
      for(i=0;i<(long long)insitu.series1.size();i++){
        for(j=0;j<(long long)insitu.tg.sealevels.at(i).size();j++){
          insitu.series1.at(i).at(j) -= insitu.tg.model.sealevels.at(i).at(j);
        }
      }

      //If timeseries_plots_desired, make plots of time series at each station.
      if(timeseries_plots_desired==1) make_timeseries_plots(insitu);

      //Now compare the residual signal from the GRACE mascons to the
      //"tide gauge minus the FES model" signal.
      compare_station_timeseries(insitu);

      break;
    }
    /////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////
    case 3:{//Compare masconFES to TGs.

      //Intended for use with type 1003, only periods from FES.

      //Use the amplitudes and phases in results to generate a
      //sealevel signal at each tide gauge station at each time.
      //This requires matching the input.epoch time (which is in GPS time)
      //to the tide gauge times (which are in UTC time).
      //It also requires that insitu_initialization() found the closest
      //grid points to each station along with their respective weighting
      //factors to be used when "interpolating" the residual mascon signal
      //to the tide gauge station's location.
      generate_input_timeseries(insitu,results,grid,1);

      //These variables are only used when time series aren't constructed!
      //insitu.tg.model.amplitudes,phases
      //This vector isn't used at all!
      //insitu.tg.output_values.at(i).at(j)

      period_info_s period_info;

      //Just checking.
      for(i=0;i<(long long)results.options.parameter.size();i++)
        if(results.options.parameter_type.at(i) != 101)
          cout<<"!!!WARNING!!! NOT ALLOWED TO LOAD NON-PERIODIC PARAMETERS!!! param_type = "<<results.options.parameter_type.at(i)<<", param = "<<results.options.parameter.at(i)<<endl;

      insitu.tg.model.omegas.clear();
      insitu.tg.model.titles.clear();
      insitu.tg.model.amplitudes.clear();
      insitu.tg.model.phases.clear();

      insitu.tg.model.periods=results.options.parameter;

      for(i=0;i<(long long)results.options.parameter.size();i++){
        is_this_period_recognized(results.options.parameter.at(i),period_info);
        insitu.tg.model.titles.push_back(period_info.title);
        insitu.tg.model.omegas.push_back(period_info.omega);
      }

      insitu.name1 = "in situ";
      insitu.name2 = "masconFES";
      insitu.series1 = insitu.tg.sealevels;
      insitu.series2 = insitu.tg.input_sealevels;

      //If timeseries_plots_desired, make plots of time series at each station.
      if(timeseries_plots_desired==1) make_timeseries_plots(insitu);

      //Now compare the residual signal from the GRACE mascons to the
      //tide gauge signal.
      compare_station_timeseries(insitu);

      break;
    }
    /////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////
    case 4:{//Compare masconFES to autoFES, using TGs stat. locations/times.

      //If run with custom grid instead of stations, need to change some vars
      //and create synth time series.
      if(grid.options.type == 0){
        cout<<"WARNING!!! CUSTOM GRID ADAPTATION ONLY WORKS FOR INSITU_COMPARISON() CASE 4, AND IT CAN'T HANDLE CUSTOM GRIDS WITH MORE THAN 1 POINT (ALTHOUGH THAT BREAKS LOTS OF OTHER CODE TOO)!!!"<<endl;
        i=0;//Hack to point to the first grid point!
        insitu.tg.grid.lat.clear();
        insitu.tg.grid.lat.push_back(grid.lat.at(i));
        insitu.tg.grid.lon.clear();
        insitu.tg.grid.lon.push_back(grid.lon.at(i));
        insitu.tg.titles.clear();
        sprintf(s,"Custom grid pt at %2.0fN, %3.0fE.",grid.lat.at(i),grid.lon.at(i));
        insitu.tg.titles.push_back(s);

        //Create synth time series.
        insitu.synth_interval = 3600;//Between data points, in seconds.
        insitu.num_synth_points = 24*365*20LL;
        //Bizarrely, extra (long long) is needed when offset = 200*365*24*3600
        //otherwise:
        //gaia.cpp: In function ‘int insitu_comparison_verification(insitu_s&, ancillary_s&, results_s&, grid_s&, model_options_s&, input_s&, plot_options_s&)’:
        //gaia.cpp:1074: warning: integer overflow in expression
        insitu.synth_offset=0;//3600*24*365*800LL;//In seconds from J2000.
        create_synth_timeseries(insitu);

        insitu.tg.nearby_grid_indices.resize(insitu.tg.grid.lat.size());
        insitu.tg.nearby_grid_distances.resize(insitu.tg.grid.lat.size());
        insitu.tg.nearby_grid_indices.at(i).push_back(i);
        insitu.tg.nearby_grid_distances.at(i).push_back(0.0);
        insitu.tg.weighting_factors.resize(insitu.tg.grid.lat.size());
        insitu.tg.weighting_factors.at(i).push_back(1.0);

        insitu.tg.sealevels.resize(insitu.tg.titles.size());
        //Hack to point to the first grid point doesn't work after/inside for loop.
        for(i=0;i<(long long)insitu.tg.sealevels.size();i++){
          insitu.tg.sealevels.at(i).resize(insitu.tg.times.at(i).size(),0.0);
        }
      }

      //Generate sealevel predictions using FES at all tide gauge stations.
      model_options_s insitu_model_options=model_options;
      //2 - load into fes_data one at a time, verbosely - debug!
      //3 - load all stations at once.
      insitu_model_options.tg_type=3;//type 3 is faster but broken!
      generate_model_timeseries(insitu,insitu_model_options);

      //Use the amplitudes and phases in results to generate a
      //sealevel signal at each tide gauge station at each time.
      //This requires matching the input.epoch time (which is in GPS time)
      //to the tide gauge times (which are in UTC time).
      //It also requires that insitu_initialization() found the closest
      //grid points to each station along with their respective weighting
      //factors to be used when "interpolating" the residual mascon signal
      //to the tide gauge station's location.
      generate_input_timeseries(insitu,results,grid,1);

      period_info_s period_info;

      //Just checking.
      for(i=0;i<(long long)results.options.parameter.size();i++)
        if(results.options.parameter_type.at(i) != 101)
          cout<<"!!!WARNING!!! NOT ALLOWED TO LOAD NON-PERIODIC PARAMETERS!!!"<<endl;

      insitu.tg.model.omegas.clear();
      insitu.tg.model.titles.clear();

      //These aren't refilled because only the timeseries are needed.
      insitu.tg.model.amplitudes.clear();
      insitu.tg.model.phases.clear();

      insitu.tg.model.periods=results.options.parameter;

      for(i=0;i<(long long)results.options.parameter.size();i++){
        is_this_period_recognized(results.options.parameter.at(i),period_info);
        insitu.tg.model.titles.push_back(period_info.title);
        insitu.tg.model.omegas.push_back(period_info.omega);
      }

      insitu.name1 = "autoFES";
      insitu.name2 = "masconFES";
      insitu.series1 = insitu.tg.model.sealevels;
      insitu.series2 = insitu.tg.input_sealevels;

      //If timeseries_plots_desired, make plots of time series at each station.
      if(timeseries_plots_desired==1) make_timeseries_plots(insitu);

      //Now compare the residual signal from the GRACE mascons to the
      //automatic FES signal.
      compare_station_timeseries(insitu);

      break;
    }
  }//End of switch-case.
}

void insitu_initialize(insitu_s &insitu,
                       grid_s &grid,
                       model_options_s &model_options,
                       ancillary_s &ancillary){
  /**********************************************************************
  Purpose: This function loads in situ station locations only.  It then
          finds the nearest grid points to each station- grid points that
          can be used to interpolate a model or my mascon signal to the
          location of each station.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  //Set these to 0 to skip loading, or 1 to load.
  insitu.loadUHSLC=0;
  insitu.loadPSMSL=0;
  insitu.loadABBAS=0;
  insitu.loadCEC=1;
  insitu.loadCanada=0;
  insitu.loadDART=0;
  insitu.loadFRAM=0;
  insitu.loadAP=0;//At the very end of this fctn!
  
  insitu.init_only=1;//1 tells load_insitu to only load locations.
  load_insitu(insitu,ancillary);
  insitu.init_only=0;//So next time load_insitu loads everything.

  int verbose=1;//1/0 turns on/off lots of detailed couts.

  long long i,j;

  //These variables are used to match up grid points with tide gauges.
  double grid_to_gauge_distance;

  //-1 is a signal to revert to the above value after grid is defined so
  //if a_o.type=0 and the loaded grid is different than specified here,
  //no problem occurs.
  if(model_options.halfwidth == -1)
    model_options.halfwidth=grid.options.lat_spacing/2;

  //Ditto.
  if(model_options.averaging_radius == -1)
    model_options.averaging_radius=2*model_options.halfwidth;

  ////////////////////////////////////////////////////////////////////////
  //For each station, loop through grid and find nearest grid points for
  //interpolation.
  ////////////////////////////////////////////////////////////////////////

  cout<<"Now finding main grid points closest to tide gauge locations (within an averaging radius of "<<model_options.averaging_radius<<" km) and computing Gaussian weighting factors (halfwidth = "<<model_options.halfwidth<<" km) for each one."<<endl<<endl;

  //Station "i" has its own vector in m_n_g_i at index "i".
  insitu.tg.nearby_grid_indices.resize(insitu.tg.grid.lat.size());
  insitu.tg.nearby_grid_distances.resize(insitu.tg.grid.lat.size());

  for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
    if(verbose) cout<<insitu.tg.titles.at(i)<<" at lat "<<insitu.tg.grid.lat.at(i)<<" and lon  "<<insitu.tg.grid.lon.at(i)<<" is near grid pts:"<<endl;

    for(j=0;j<(long long)grid.lat.size();j++){
      grid_to_gauge_distance = planet_avg_radius*spherical_distance(grid.lat.at(j),grid.lon.at(j),insitu.tg.grid.lat.at(i),insitu.tg.grid.lon.at(i));
      //First, make sure the distance from this gr pt to gauge is < avg rad
      if(grid_to_gauge_distance <= model_options.averaging_radius){
        //cout<<"grid_to_gauge_distance = "<<grid_to_gauge_distance<<", model_options.averaging_radius = "<<model_options.averaging_radius<<", grid.elev["<<j<<"] = "<<grid.elev.at(j)<<endl;
        //Only including points if they have wcover > 0.
        if(grid.wcover.at(j) > 0.0){

          //2009-09-09 - Added division by wcover.
          //Just checking...
          if(grid.wcover.at(j) == 0.0) cout<<"!!!!WARNING!!! ZERO wcover FOR Lat: "<<grid.lat.at(j)<<" Lon: "<<grid.lon.at(j)<<", km to station: "<<grid_to_gauge_distance<<", km to ocean: "<<grid.distance_to_ocean.at(j)<<endl;

          insitu.tg.nearby_grid_indices.at(i).push_back(j);
          insitu.tg.nearby_grid_distances.at(i).push_back(grid_to_gauge_distance);
          if(grid.elev.at(j) > 0){
            if(verbose) cout<<"Lat: "<<grid.lat.at(j)<<" Lon: "<<grid.lon.at(j)<<", km to station: "<<grid_to_gauge_distance<<", km to ocean: "<<grid.distance_to_ocean.at(j)<<endl;
          }
          else{
            if(verbose) cout<<"Lat: "<<grid.lat.at(j)<<" Lon: "<<grid.lon.at(j)<<", km to station: "<<grid_to_gauge_distance<<", km to land: "<<grid.distance_to_land.at(j)<<endl;
          }
        }
        //else cout<<"grid.distance_to_ocean["<<j<<"] = "<<grid.distance_to_ocean.at(j)<<" but 0.5*grid.options.lat_spacing = "<<0.5*grid.options.lat_spacing<<endl;
      }
    }//End of loop through grid.
    if(verbose) cout<<insitu.tg.titles.at(i)<<" has "<<insitu.tg.nearby_grid_indices.at(i).size()<<" grid points next to it for interpolation."<<endl<<endl;
  }//End of loop through tide gauge stations.

  //Make a list of weighting factors for each grid point found near
  //each station.  These weighting factors are used to generate a
  //Gaussian-weighted sum of the amplitudes and phases of the
  //residual mascon signal.
  //Loop through tide gauge stations.
  insitu.tg.weighting_factors.resize(insitu.tg.grid.lat.size());

  for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
    insitu.tg.weighting_factors.at(i).resize(insitu.tg.nearby_grid_indices.at(i).size());
    //Loop through nearest grid points.
    for(j=0;j<(long long)insitu.tg.nearby_grid_indices.at(i).size();j++){

      //2009-09-09 Changed to inverse distance weighting.
      insitu.tg.weighting_factors.at(i).at(j) = pow(insitu.tg.nearby_grid_distances.at(i).at(j),-4);
      //Old statement, used model halfwidth and gaussian.
      //insitu.tg.weighting_factors.at(i).at(j) = exp(-pow(insitu.tg.nearby_grid_distances.at(i).at(j),2)/(2*pow(model_options.halfwidth,2)));
      //cout<<"m_n_g_d["<<i<<"]["<<j<<"]: "<<insitu.tg.nearby_grid_distances.at(i).at(j)<<endl;
      //cout<<"m_w_f["<<i<<"]["<<j<<"]: "<<insitu.tg.weighting_factors.at(i).at(j)<<endl<<endl;
    }
  }

  cout << "Finished with in situ initialization."<<endl;
}

void load_insitu(insitu_s &insitu, ancillary_s &ancillary){
  /**********************************************************************
  Purpose: This function loads in situ data, such as tide gauge data.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  char s[max_length];
  string basefolder(gaiafolder());
  string file, currentfolder;

  //These variables are used to search the cache folder for tide gauge data.
  DIR *cacheDIR;
  struct dirent *cacheDirEnt;
  FILE *in_fp;
  string newfile = insitu.cachefolder;
  string filename;//stores JUST the filename of cache file (not the path).
  FILE *new_fp;

  //These variables are used to search the tide gauge data folder.
  DIR *dataDIR;
  struct dirent *dataDirEnt;
  string temp_string;
  long long i=0,j,filecounter;

  //These variables are used to read data from the files in the tide gauge
  //data folder:
  string line1,error;
  double time,mean,another_double;
  long long time_long;
  //double temp,salinity;
  int sealevel,pos;
  ifstream input_file;//Used to grab strings from the file.
  istringstream stream1;//Used to grab data from the strings.
  long long k;

  double temp_double;
  long long temp_long;
  //The tide gauge files store the date as a julian day- day since jan1.
  //This means I need to record the character year in julian_year.
  //So that I don't have to recompute the julian date of "midnight on jan 1"
  //of each year on each line, I'm storing the previous_julian_year so that
  //this is skipped unless the current julian_year != previous_julian_year
  int julian_year,previous_julian_year;
  //In seconds past J2000.0.
  long long current_character_time;
  //pressure_time is the result of converting characters in air file to secs.
  //character_time is the same thing with characters from sealevel file.
  vector <long long> pressure_time, character_time;

  //I just added fes_julian_times to the tide gauge structure, and easily computing it requires me to store the "fes" julian date of the current year's jan 1 at 00:00.00.  (The FES Julian date is relative to jan-1-1950-00:00.00.)
  double current_fes_julian_epoch;

  //Used to read air pressure data:
  int skip_air_pressures;
  vector<string> station_pressure_filenames;//Matches station_filenames.
  double airpressure;
  long long l;

  //This vector contains abbreviations which are used to tell which station
  //a file is describing.  If the filename contains that abbreviation, it's
  //assumed that it contains data about the corresponding station.
  //Oh, and airpressure files have lowercase filenames, so they need a
  //separate vector.
  vector <string> stations,lowercase_stations;

  //List of all .CHK, sqd files in the tide gauge data folder.
  vector <string> chk_filenames,sqd_filenames;

  //Vector that contains all the filenames in the tide gauge folder that
  //contain the abbreviation (held in the stations vector).  This vector
  //is cleared everytime the tide gauge folder is searched for files
  //from a different station.
  vector<string> station_filenames;

  //When loading multiple types of tide gauge data, record the current
  //index at which data should be added to titles/sealevels/times.
  long long csi=0;

  //UHSLC data is stored so that each station has its own folder.  This
  //vector stores a list of the (absolute) names of these folders.
  vector<string> folder_list;
  //This vector stores a list of the (relative) names of the files inside
  //each of the folders in folder_list.  This vector is cleared b/w folders.
  vector<string> file_list;
  //UHSLC data has a GMT offset (hours from GMT time) in each station's file.
  double gmt_offset;
  //Although not technically a "character" time (time_conversion routine
  //used is not char2sec.e but cal2sec.e), the UHSLC reading routines use
  //current_character_time to store the UTC time of midnight or noon in each
  //line.  This time is initially compared to the previous time in order to
  //make sure that I can get away with only using cal2sec.e once per file.
  long long previous_character_time;
  string prev_time;//debug.

  int temp1,temp2;//Used when reading UHSLC header information.

  //Used when loading Cecilia's time series data.
  long long cecilia_utc_epoch; double cecilia_fes_epoch;

  char* f_return;//Used to stop fgets return value warning.
 
  string token;

  //Useless, but stops warning.
  if(ancillary.topo.format == 1 and 1==2) cout<<"ignore me"<<endl;

  //Custom time conversion functions require this structure as a container.
  time_conversion_s time_conversion; time_conversion.initialized=0;
  time_conversion.calendar_time.resize(6,0);

  //This is a custom list of locations, so designate grid type 0.
  //Was already set in insitu_initialize(), but just to be sure..
  insitu.tg.grid.options.type=0;

  //Now that locations are loaded in insitu_initialize(), it's a good
  //idea to clear the variables that were loaded in that function, so they
  //don't screw up loading in this function.  But first copy the location
  //vectors from insitu_initialize() to temp locations init_lat/lon so that
  //they can be compared to the lat and lon vectors that are made here.
  vector <double> init_lat,init_lon;
  init_lat = insitu.tg.grid.lat;
  init_lon = insitu.tg.grid.lon;
  cout<<"SHOULD PROBABLY ADD A LOT MORE CLEARS HERE, OR SET ENTIRE INSITU STRUCT TO A BLANK VERSION"<<endl;
  insitu.tg.titles.clear();
  insitu.tg.grid.lat.clear();
  insitu.tg.grid.lon.clear();
  insitu.tg.grid.elev.clear();

  //Search for previously loaded insitu data in the cache, but only if
  //noload == 0.
  if(insitu.noload == 0){

    //Check to see if the cache contains a file with the same options
    //as the ones loaded in region_options.  If so, set i=1.
    i=0;//Init as 0 because it hasn't found matching region in cache yet.

    //First, figure out exactly what filename we're looking for by
    //creating a filename that corresponds to currently selected options.
    filename = create_tg_cache_filename(insitu);

    //Open the cache folder.
    cacheDIR = opendir(insitu.cachefolder.c_str());

    //Examine each file, see if it contains the desired in situ info.
    cacheDirEnt = readdir(cacheDIR);
    while(cacheDirEnt != NULL && i == 0){
      //If filename matches specified in situ options, set i=1 to exit loop.
      if(strcmp(filename.c_str(),cacheDirEnt->d_name)==0) i = 1;
      cacheDirEnt = readdir(cacheDIR);
    }

    // Release the open folder.
    closedir(cacheDIR);

    //If matching file was found, load it and skip rest of function.
    if(i == 1){
      cout<<"Loading tide gauge data from cache..."<<endl;
      //Prepend directory to filename, then open.
      newfile.append(filename);
      in_fp = fopen(newfile.c_str(),"r");

      //Load the first header line which contains # of stations.
      f_return=fgets(s,max_length,in_fp);
      sscanf(s,"%lld", &k);
      //Use # of stations to resize vectors.
      insitu.tg.titles.resize(k);
      insitu.tg.grid.lat.resize(k);
      insitu.tg.grid.lon.resize(k);
      insitu.tg.grid.elev.resize(k);
      insitu.tg.times.resize(k);
      insitu.tg.fes_julian_times.resize(k);
      insitu.tg.sealevels.resize(k);
      insitu.tg.airpressures.resize(k);
      insitu.tg.salinities.resize(k);
      //Including amp/phase vectors.
      insitu.tg.amplitudes.resize(k);
      insitu.tg.phases.resize(k);
      insitu.tg.tide_booleans.resize(k);
      insitu.tg.period_titles.resize(k);
      insitu.tg.periods.resize(k);

      //Read in different station titles, then the number of times at that
      //station, then the coordinates and elevation of the station.
      for(j=0;j<(long long)insitu.tg.titles.size();j++){
        //Read station titles first.
        f_return=fgets(s,max_length,in_fp);
        insitu.tg.titles.at(j) = s;
        //Remove the \n at the end of the line.
        if((pos=insitu.tg.titles.at(j).find("\n"))!=(int)string::npos){
          insitu.tg.titles.at(j).swap(insitu.tg.titles.at(j).erase(pos,2));
        }
        //Now read the number of data points at this station, use to resize.
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lld", &k);
        if(k>0){
          insitu.tg.times.at(j).resize(k);
          insitu.tg.fes_julian_times.at(j).resize(k);
          insitu.tg.sealevels.at(j).resize(k);
          insitu.tg.airpressures.at(j).resize(k);
        }
        //Now read the number of amps at this station, use to resize.
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lld", &k);
        if(k>0){
          insitu.tg.periods.at(j).resize(k);
          insitu.tg.period_titles.at(j).resize(k);
          insitu.tg.amplitudes.at(j).resize(k);
          insitu.tg.phases.at(j).resize(k);
          insitu.tg.tide_booleans.at(j).resize(k);
        }
        //Now read the coordinates and elevation of this station.
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf", &mean);//use the double mean as a temp variable.
        insitu.tg.grid.lat.at(j) = mean;
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf", &mean);//use the double mean as a temp variable.
        insitu.tg.grid.lon.at(j) = mean;
        f_return=fgets(s,max_length,in_fp);
        sscanf(s,"%lf", &mean);//use the double mean as a temp variable.
        insitu.tg.grid.elev.at(j) = mean;
      }

      if(insitu.init_only != 1){
        //Load the next line separately because it contains no data.
        f_return=fgets(s,max_length,in_fp);
        //Loop through all stations, loading timeseries for each station.
        for(j=0;j<(long long)insitu.tg.grid.lat.size();j++){
          //Ignore the first line, it's just "New Station:"
          f_return=fgets(s,max_length,in_fp);
          //Now read the times, sealevels and airpressures for this station.
          for(i=0;i<(long long)insitu.tg.times.at(j).size();i++){
            f_return=fgets(s,max_length,in_fp);
            sscanf(s,"%lld %lf %le %le", &time_long, &mean, &time, &another_double);
            insitu.tg.times.at(j).at(i) = time_long;
            insitu.tg.sealevels.at(j).at(i) = mean;
            insitu.tg.airpressures.at(j).at(i) = time;
            insitu.tg.fes_julian_times.at(j).at(i) = another_double;
          }
        }
        //Load the next line separately because it contains no data.
        f_return=fgets(s,max_length,in_fp);
        //Loop through all stations, loading amps/phases for each station
        for(j=0;j<(long long)insitu.tg.grid.lat.size();j++){
          //Ignore the first line, it's just "New Station:"
          f_return=fgets(s,max_length,in_fp);
          //Now read the period, amp, phase for this station.
          for(i=0;i<(long long)insitu.tg.times.at(j).size();i++){
            //Read period title.
            f_return=fgets(s,max_length,in_fp);
            insitu.tg.period_titles.at(j).at(i) = s;
            //Read per, amp, phase, tide boolean.
            f_return=fgets(s,max_length,in_fp);
            sscanf(s,"%le %le %le %lld", &mean, &time, &another_double, &time_long);
            insitu.tg.periods.at(j).at(i) = mean;
            insitu.tg.amplitudes.at(j).at(i) = time;
            insitu.tg.phases.at(j).at(i) = another_double;
            insitu.tg.tide_booleans.at(j).at(i) = time_long;
          }
        }
      }

      fclose(in_fp);
      printf("Finished. %lld tide gauge stations loaded.",(long long)insitu.tg.grid.lat.size());
      cout<<endl;
      return;
    }//End of "If a matching cache file was found" statements.
  }//End of "if cache loading is enabled" statements.
  
  //WHEN INSITU CACHE WORKS AGAIN, MOVE THIS SECTION TO BOTTOM OF FUNCTION!
  //After loading tide gauge data from original files, save it to the cache
  //so it can be loaded more quickly the next time.
      //if(insitu.init_only != 1){
  if(1==2){//Skipping this because I'm turning off in situ cache for now.
    filename=create_tg_cache_filename(insitu);
    newfile.append(filename);
    cout<<"Writing tide gauge data to cache file: "<<newfile<<" ..."<<endl;
    new_fp = fopen(newfile.c_str(),"w");
    //Write insitu.tg.grid.lat.size() as the first line.
    //This is the number of tide gauge stations.
    fprintf(new_fp,"%5lld\n",(long long)insitu.tg.grid.lat.size());
    //Then save information about each station like its title, # of data
    //points, coordinates and elevation.
    for(j=0;j<(long long)insitu.tg.grid.lat.size();j++){
      fprintf(new_fp,"%s\n",insitu.tg.titles.at(j).c_str());
      fprintf(new_fp,"%lld\n",(long long)insitu.tg.times.at(j).size());
      fprintf(new_fp,"%lld\n",(long long)insitu.tg.amplitudes.at(j).size());
      fprintf(new_fp,"%20.12f\n",insitu.tg.grid.lat.at(j));
      fprintf(new_fp,"%20.12f\n",insitu.tg.grid.lon.at(j));
      fprintf(new_fp,"%20.12f\n",insitu.tg.grid.elev.at(j));
    }
    //Now record sealevel heights at all stations at all times.
    fprintf(new_fp,"UTC times and corresponding sealevel heights (in cm) for all stations follow in the order that stations are listed above.\n");
    for(j=0;j<(long long)insitu.tg.grid.lat.size();j++){
      fprintf(new_fp,"New Station:\n");
      for(i=0;i<(long long)insitu.tg.times.at(j).size();i++){
        fprintf(new_fp,"%lld   %7.1f %20.10e %20.10e\n",insitu.tg.times.at(j).at(i),insitu.tg.sealevels.at(j).at(i),insitu.tg.airpressures.at(j).at(i),insitu.tg.fes_julian_times.at(j).at(i));
      }
    }
    //Now record period titles, periods, amps and phases at all stations.
    fprintf(new_fp,"Period titles (on their own lines) then periods, amps and phases (all on same line) for all stations follow.\n");
    for(j=0;j<(long long)insitu.tg.grid.lat.size();j++){
      fprintf(new_fp,"New Station:\n");
      for(i=0;i<(long long)insitu.tg.amplitudes.at(j).size();i++){
        fprintf(new_fp,"%s\n",insitu.tg.period_titles.at(j).at(i).c_str());
        fprintf(new_fp,"%20.10e %20.10e %20.10e %d\n", insitu.tg.periods.at(j).at(i), insitu.tg.amplitudes.at(j).at(i), insitu.tg.phases.at(j).at(i), insitu.tg.tide_booleans.at(j).at(i));
      }
    }

    fclose(new_fp);
    cout<<"Finished."<<endl;
  }

  ////////////////////////////////////////////////////////////////////////
  //Load hourly td gauge data from University of Hawaii Sea Level Center.
  ////////////////////////////////////////////////////////////////////////
  if(insitu.loadUHSLC==1){
    cout << "Loading hourly UHSLC tide gauge data."<<endl;

    cout<<"WARNING: SOME UHSLC DATA HAS BEEN MOVED UP TO TIDE_GAUGES BECAUSE IT DOESN'T APPEAR TO BE IN THE UHSLC FORMAT.  FIGURE OUT WHERE THIS DATA CAME FROM AND MAYBE CATEGORIZE IT DIFFERENTLY"<<endl;

    cout<<"Make sure I'm using the GMT offset correctly- dividing by ten?, subtracting or adding it to the time values?  This may be difficult to diagnose at first because some tide stations just use 0000 as their offset."<<endl;

    //UHSLC data will be entered into the * vectors at this index:
    csi = (long long)insitu.tg.titles.size();

    //UHSLC data is stored in data/tide_gauges/UHSLC, but the *.dat files are
    //all kept in separate folders produced by Ubuntu's zip extract utility.
    //Search for these folders now and build a list of them in folder_list
    basefolder = gaiafolder();
    basefolder.append(insitu.tg.basefolder);
    basefolder.append("UHSLC/");
    dataDIR = opendir(basefolder.c_str());
    //Search the UHSLC folder for files that contain "a.zip_FILES".  Files
    //that contain this string are folders that hold a single station's data.
    folder_list.clear();
    dataDirEnt = readdir(dataDIR);
    while(dataDirEnt != NULL){
      strcpy(s,dataDirEnt->d_name);
      temp_string = s;
      if(((int)temp_string.find(".zip_FILES"))!=(int)string::npos) folder_list.push_back(s);
      dataDirEnt = readdir(dataDIR);
    }//End of while loop through files

    //Close the UHSLC folder.
    closedir(dataDIR);

    //Sort the folders alphabetically.
    sort(folder_list.begin(),folder_list.end());

    //debug.
    if(1==2){
      cout<<"Folders are: "<<endl;
      for(i=0;i<(long long)folder_list.size();i++) cout<<folder_list.at(i)<<endl;
      cout<<endl;
    }

    //Add enough entries to * vectors to fit data from each folder.
    if(insitu.init_only != 1){
      insitu.tg.sealevels.resize(csi+(long long)folder_list.size());
      insitu.tg.times.resize(csi+(long long)folder_list.size());
      insitu.tg.fes_julian_times.resize(csi+(long long)folder_list.size());
      insitu.tg.airpressures.resize(csi+(long long)folder_list.size());
    }

    //Now that folders have been identified, open each one in sequence and
    //read the files inside it.
    for(i=0;i<(long long)folder_list.size();i++){
      //cout<<"Loading UHSLC files from folder "<<folder_list.at(i)<<endl;

      //Build a list of files in the current folder.
      currentfolder = basefolder;
      currentfolder.append(folder_list.at(i));
      dataDIR = opendir(currentfolder.c_str());
      file_list.clear();
      dataDirEnt = readdir(dataDIR);
      while(dataDirEnt != NULL){
        strcpy(s,dataDirEnt->d_name);
        temp_string = s;
        if((int)temp_string.length() > 2) file_list.push_back(s);
        dataDirEnt = readdir(dataDIR);
      }//End of while loop through files
      //Close the current folder.
      closedir(dataDIR);

      //Sort the files alphabetically.
      sort(file_list.begin(),file_list.end());

      //debug.
      if(1==2){
        cout<<"Files are: "<<endl;
        for(j=0;j<(long long)file_list.size();j++) cout<<file_list.at(j)<<endl;
        cout<<endl;
      }

      //Read data from each file.
      for(j=0;j<(long long)file_list.size();j++){
        file = basefolder;
        file.append(folder_list.at(i));
        file.append("/");
        file.append(file_list.at(j));
        //cout<<"Loading UHSLC file "<<file_list.at(j)<<endl;
        //Open input file for reading.
        input_file.open(file.c_str());
        //Warn if file doesn't open correctly.
        if(input_file == NULL) cout<<"The UHSLC file, "<< file << ", failed to open."<<endl;

        //If this is the first file in this folder, read station information
        //from the header and save it.
        if(j==0){
          //Read first header line into line1.
          getline(input_file,line1);
          //cout<<line1<<endl;
          //Read name of the current tide gauge.
          temp_string = line1.substr(5,18);
          //cout<<"Loading data from UHSLC station: "<<temp_string<<endl;
          insitu.tg.titles.push_back(temp_string);

          //Convert latitude from degrees, minutes, tenths to degrees.decimals.
          stream1.clear();
          stream1.str(line1.substr(49,2));
          stream1 >> temp1;//Degrees
          stream1.clear();
          stream1.str(line1.substr(51,3));
          stream1 >> temp2;//Minutes * 10
          stream1.clear();
          temp_double = (double)temp1 + (double)temp2/600.0;
          //Read hemisphere for negative sign.
          temp_string = line1.substr(54,1);
          //cout<<"hemisphere: "<<temp_string<<endl;
          if(((int)temp_string.find("S"))!=(int)string::npos) temp_double *= -1;
          //cout<<"lat: "<<temp_double<<endl;
          insitu.tg.grid.lat.push_back(temp_double);

          //Convert longitude from degrees, minutes, tenths to degrees.decimals.
          stream1.clear();
          stream1.str(line1.substr(56,3));
          stream1 >> temp1;//Degrees
          stream1.clear();
          stream1.str(line1.substr(59,3));
          stream1 >> temp2;//Minutes * 10
          stream1.clear();
          temp_double = (double)temp1 + (double)temp2/600.0;
          //Read hemisphere for negative sign.
          temp_string = line1.substr(62,1);
          //cout<<"hemisphere: "<<temp_string<<endl;
          if(((int)temp_string.find("W"))!=(int)string::npos) temp_double *= -1;
          //Convert from -180..180 to 0..360.
          if(temp_double < 0.0) temp_double += 360.0;

          //cout<<"lon: "<<temp_double<<endl;
          insitu.tg.grid.lon.push_back(temp_double);

          //Record GMT offset
          stream1.clear();
          stream1.str(line1.substr(64,4));
          stream1 >> gmt_offset;//Hours * 10
          //Converting to seconds.
          gmt_offset = gmt_offset/10.0*3600;
          //cout<<"gmt_offset: "<<gmt_offset<<endl;
          stream1.clear();

          //Find elevation at this station.
          insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(i+csi),insitu.tg.grid.lon.at(i+csi),ancillary));

        }//End of "if this is the first file in each folder" statements.
        else getline(input_file,line1); //Else ignore header line.
        
        if(insitu.init_only != 1){

          //Read time series data from each file.
          filecounter=0;

          //Initialize time conversion minutes,seconds to 0.  Make sure
          //these values aren't over-ridden by other values, because I'm not
          //going to waste time in the loops re-writing the 0s over and over.
          time_conversion.calendar_time.resize(6,0);
          time_conversion.calendar_time.at(4) = 0;//minutes
          time_conversion.calendar_time.at(5) = 0;//seconds
          while(getline(input_file,line1)){
            filecounter++;
            //Read the 12 sea level measurements in mm.
            //cout<<"l1: "<<line1.substr(0,50)<<endl;
            temp_string = line1.substr(20,60);

            //If all 12 values are masks, ignore this line.
            if(((int)temp_string.find("9999 9999 9999 9999 9999 9999 9999 9999 9999 9999 9999 9999"))==(int)string::npos){

              //Convert "calendar time" at midnight or noon to secs past J2000.
              stream1.clear();
              stream1.str(line1.substr(11,4));//year
              stream1 >> time_long;
              time_conversion.calendar_time.front() = time_long;
              //cout<<"year: "<<line1.substr(11,4)<<" = "<<time_long<<endl;
              stream1.clear();
              stream1.str(line1.substr(15,2));//month
              stream1 >> time_long;
              //cout<<"month: "<<time_long<<endl;
              time_conversion.calendar_time.at(1) = time_long;
              stream1.clear();
              stream1.str(line1.substr(17,2));//day
              stream1 >> time_long;
              //cout<<"day: "<<time_long<<endl;
              time_conversion.calendar_time.at(2) = time_long;
              stream1.clear();

              //If this is 2nd record, it's noon. Otherwise, it's midnight (0).
              if(((int)line1.substr(19,1).find("2"))!=(int)string::npos) time_conversion.calendar_time.at(3) = 12;//hours - noon
              else time_conversion.calendar_time.at(3) = 0;//hours - midnight
              cal2utc(time_conversion);//Convert calendar time to UTC seconds.
              previous_character_time = current_character_time;
              current_character_time = (long long)((double)time_conversion.utc_seconds + gmt_offset);

              cal2fes_jul(time_conversion);//Convert to FES Julian date.

              //cout<<"current_character_time: "<<current_character_time<<endl;

              /*//Check that this time is exactly 12 hours after previous one.
              //NOTE: This check doesn't work if lines with 9999 are skipped.
              if(filecounter>1 and current_character_time - previous_character_time != 43200){
                cout<<"Time in file: "<<line1.substr(11,10)<<" turns into: "<<time_conversion.calendar_time.front()<<" "<<time_conversion.calendar_time.at(1)<<" "<<time_conversion.calendar_time.at(2)<<" "<<time_conversion.calendar_time.at(3)<<endl<<"cal2utc gives: "<<time_conversion.utc_seconds<<endl;
                cout<<"!WARNING!!  current_character_time - previous_character_time = "<<(double)(current_character_time - previous_character_time)/3600.0<<" hours."<<endl;
                cout<<"prev_time: "<<prev_time<<endl<<endl;
              }
              prev_time = line1.substr(11,10);*/

              //Loop through sea level measurements on this line.
              //Only bother to save values if sealevel != 9999
              for(k=0;k<12;k++){
                stream1.clear();
                stream1.str(line1.substr(21+k*5,4));
                //cout<<"UTC: "<<current_character_time+k*3600<<" FES Jul: "<<time_conversion.fes_jul_days + (double)k/24.0<<" sea level: "<<line1.substr(21+k*5,4)<<endl;
                stream1 >> temp_long;
                if(temp_long != 9999){
                  //Divide by 10 to change to cm, then push_back sea level
                  //and the time (sum of current line's time and k*(1 hour)).
                  insitu.tg.times.at(i+csi).push_back(current_character_time+k*3600);
                  insitu.tg.fes_julian_times.at(i+csi).push_back(time_conversion.fes_jul_days + (double)k/24.0);
                  insitu.tg.sealevels.at(i+csi).push_back((double)temp_long/10.0);//mm -> cm.
                }
              }//End of loop through sea level measurements.
            }//End of "if this line isn't all 9999's" statement.
          }//End of loop through lines in the current file.
        }
        input_file.close();//Close the current file.
      }//End of loop through files.

      if(insitu.init_only != 1){
        //Calculate and subtract the mean.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi).size();k++) mean += insitu.tg.sealevels.at(i+csi).at(k);
        mean /= (double)insitu.tg.sealevels.at(i+csi).size();
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi).size();k++) insitu.tg.sealevels.at(i+csi).at(k) -= mean;

        //Fill in airpressure vector... even though I might never use it...
        insitu.tg.airpressures.at(i+csi).resize(insitu.tg.sealevels.at(i+csi).size(),-66.6);

        cout<<"Station #"<<i+csi+1<<" ("<<insitu.tg.titles.at(i+csi)<<") has "<<insitu.tg.sealevels.at(i+csi).size()<<" sea level measurements."<<endl;
      }
    }//End of loop through folders.

    cout<<"Finished loading UHSLC tide gauge data."<<endl;
  }//End of UHSLC loading.
  ////////////////////////////////////////////////////////////////////////
  //Load tide gauge data from the Permanent Service for Mean Sea Level
  ////////////////////////////////////////////////////////////////////////
  if(insitu.loadPSMSL==1){
    cout << "Loading PSMSL tide gauge data."<<endl;

    //PSMSL data will be entered into the * vectors at this index:
    csi = (long long)insitu.tg.titles.size();

    //PSMSL data is stored in data/tide_gauges/PSMSL, but the *.lst files are
    //all kept in separate folders produced by Ubuntu's zip extract utility.
    //Search for these folders now and build a list of them in folder_list
    basefolder = gaiafolder();
    basefolder.append(insitu.tg.basefolder);
    basefolder.append("PSMSL/");
    //cout<<"Searching folder "<<basefolder<<endl;
    dataDIR = opendir(basefolder.c_str());
    //Search the PSMSL folder for files that contain "a.zip_FILES".  Files
    //that contain this string are folders that hold a single station's data.
    folder_list.clear();
    dataDirEnt = readdir(dataDIR);
    while(dataDirEnt != NULL){
      strcpy(s,dataDirEnt->d_name);
      temp_string = s;
      //cout<<"Found "<<s<<endl;
      if(((int)temp_string.find(".zip_FILES"))!=(int)string::npos) folder_list.push_back(s);
      dataDirEnt = readdir(dataDIR);
    }//End of while loop through files

    //Close the PSMSL folder.
    closedir(dataDIR);

    //Sort the folders alphabetically.
    sort(folder_list.begin(),folder_list.end());

    //debug.
    if(1==2){
      cout<<"Folders are: "<<endl;
      for(i=0;i<(long long)folder_list.size();i++) cout<<folder_list.at(i)<<endl;
      cout<<endl;
    }

    //Add enough entries to * vectors to fit data from each folder.
    if(insitu.init_only != 1){
      insitu.tg.sealevels.resize(csi+(long long)folder_list.size());
      insitu.tg.times.resize(csi+(long long)folder_list.size());
      insitu.tg.fes_julian_times.resize(csi+(long long)folder_list.size());
      insitu.tg.airpressures.resize(csi+(long long)folder_list.size());
    }

    //Now that folders have been identified, open each one in sequence and
    //read the files inside it.
    vector<string> index_file_list;
    for(i=0;i<(long long)folder_list.size();i++){
      //cout<<"Loading PSMSL files from folder "<<folder_list.at(i)<<endl;

      //Build a list of files in the current folder.
      currentfolder = basefolder;
      currentfolder.append(folder_list.at(i));
      dataDIR = opendir(currentfolder.c_str());
      file_list.clear(); index_file_list.clear();
      dataDirEnt = readdir(dataDIR);
      while(dataDirEnt != NULL){
        strcpy(s,dataDirEnt->d_name);
        temp_string = s;
        //Each folder has one file that doesn't contain data, and it's
        //always 6 chars or less. Real data files have >6 chars. PLUS 4 in extension!
        if((int)temp_string.length() > 10) file_list.push_back(s);
        else if((int)temp_string.length() > 3) index_file_list.push_back(s);
        dataDirEnt = readdir(dataDIR);
      }//End of while loop through files
      //Close the current folder.
      closedir(dataDIR);

      //Sort the files alphabetically.
      sort(file_list.begin(),file_list.end());

      //Better only have one index file.
      if((int)index_file_list.size() != 1) cout<<"WARNING!!! index_file_list.size() = "<<index_file_list.size()<<endl;

      //debug.
      if(1==2){
        cout<<"Files are: "<<endl;
        for(j=0;j<(long long)file_list.size();j++) cout<<file_list.at(j)<<endl;
        cout<<endl;
      }
      
      //Read location, name from index file.
      file = basefolder;
      file.append(folder_list.at(i));
      file.append("/");
      file.append(index_file_list.front());
      //cout<<"Loading PSMSL index file "<<index_file_list.front()<<endl;
      //Open input file for reading.
      in_fp = fopen(file.c_str(),"r");
      //Warn if file doesn't open correctly.
      if(in_fp == NULL) cout<<"The PSMSL index file, "<< file << ", failed to open."<<endl;
      //Ignore header lines.
      int headerlines = 4;
      for(j=0;j<headerlines;j++) f_return=fgets(s,max_length,in_fp);

      f_return=fgets(s,max_length,in_fp);
      token = strtok(s, ",");
    
      cout<<"Loading data from PSMSL station: "<<token<<endl;
      insitu.tg.titles.push_back(token);
      token = strtok(NULL, " ");//Read country.
      token = strtok(NULL, " ");//Read gloss no.
      token = strtok(NULL, " ");//Read lat.
      sscanf(token.c_str(),"%lf", &temp_double);
      //cout<<"lat: "<<temp_double<<endl;
      insitu.tg.grid.lat.push_back(temp_double);

      token = strtok(NULL, " ");//Read lon.
      sscanf(token.c_str(),"%lf", &temp_double);
      //cout<<"lon: "<<temp_double<<endl;
      insitu.tg.grid.lon.push_back(temp_double);
      
      fclose(in_fp);

      //Find elevation at this station.
      insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(i+csi),insitu.tg.grid.lon.at(i+csi),ancillary));

      //Only read other files if actually loading data. 
      if(insitu.init_only != 1){
        //Initialize time conversion minutes,seconds to 0.  Make sure
        //these values aren't over-ridden by other values, because I'm not
        //going to waste time in the loops re-writing the 0s over and over.
        time_conversion.calendar_time.resize(6,0);

        //Read data from each file.
        for(j=0;j<(long long)file_list.size();j++){
          file = basefolder;
          file.append(folder_list.at(i));
          file.append("/");
          file.append(file_list.at(j));
          //cout<<"Loading PSMSL file "<<file_list.at(j)<<endl;
          //Open input file for reading.
          in_fp = fopen(file.c_str(),"r");
          //Warn if file doesn't open correctly.
          if(in_fp == NULL) cout<<"The PSMSL file, "<< file << ", failed to open."<<endl;

          //Ignore header lines.
          int type=1;//default to the first one? might as well.
          string hmsdiv;
          if(((int)folder_list.at(i).find("g153"))!= (int)string::npos) type = 1;
          else if(((int)folder_list.at(i).find("w9109"))!= (int)string::npos) type = 2;
          if(type == 1){
            headerlines = 13;
            hmsdiv = ".";
          }
          else if(type == 2){
            headerlines = 20;
            hmsdiv = ":";
          }
          for(int cnt4=0;cnt4<headerlines;cnt4++) f_return=fgets(s,max_length,in_fp);
          
          //Read time series data from each file.
          while(fgets(s,max_length,in_fp)!=NULL){
            temp_string = s;
            //If value equals mask, ignore this line.
            if(((int)temp_string.find("-99.000N"))==(int)string::npos){
              token = strtok(s, " ");//Read number.
              
              //Convert "calendar time" at midnight or noon to secs past J2000.
              token = strtok(NULL, "/");//Read year.
              sscanf(token.c_str(),"%lld", &time_long);
              //cout<<"year: "<<time_long<<endl;
              time_conversion.calendar_time.front() = time_long;

              token = strtok(NULL, "/");//Read month.
              sscanf(token.c_str(),"%lld", &time_long);
              //cout<<"month: "<<time_long<<endl;
              time_conversion.calendar_time.at(1) = time_long;
              
              token = strtok(NULL, " ");//Read day.
              sscanf(token.c_str(),"%lld", &time_long);
              //cout<<"day: "<<time_long<<endl;
              time_conversion.calendar_time.at(2) = time_long;
              
              token = strtok(NULL, hmsdiv.c_str());//Read hours.
              sscanf(token.c_str(),"%lld", &time_long);
              //cout<<"hours: "<<time_long<<endl;
              time_conversion.calendar_time.at(3) = time_long;
              
              token = strtok(NULL, hmsdiv.c_str());//Read minutes.
              sscanf(token.c_str(),"%lld", &time_long);
              //cout<<"minutes: "<<time_long<<endl;
              time_conversion.calendar_time.at(4) = time_long;
              
              token = strtok(NULL, " ");//Read seconds.
              sscanf(token.c_str(),"%lld", &time_long);
              //cout<<"seconds: "<<time_long<<endl;
              time_conversion.calendar_time.at(5) = time_long;
              
              cal2utc(time_conversion);//Convert calendar time to UTC seconds.
              previous_character_time = current_character_time;
              current_character_time = (long long)((double)time_conversion.utc_seconds + gmt_offset);

              cal2fes_jul(time_conversion);//Convert to FES Julian date.

              //cout<<"current_character_time: "<<current_character_time<<endl;

              token = strtok(NULL, " ");//Read sea level height.
              double seaheight;
              sscanf(token.c_str(),"%lf", &seaheight);
              //cout<<"UTC: "<<current_character_time<<" FES Jul: "<<time_conversion.fes_jul_days<<" sea level (m): "<<seaheight<<endl<<endl;
              insitu.tg.times.at(i+csi).push_back(current_character_time);
              insitu.tg.fes_julian_times.at(i+csi).push_back(time_conversion.fes_jul_days);
              insitu.tg.sealevels.at(i+csi).push_back((double)seaheight*100.0);//m -> cm.
            }//End of "if this line isn't masked" statement.
          }//End of loop through lines in the current file.
          fclose(in_fp);
        }//End of loop through files.

        //Calculate and subtract the mean.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi).size();k++) mean += insitu.tg.sealevels.at(i+csi).at(k);
        mean /= (double)insitu.tg.sealevels.at(i+csi).size();
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi).size();k++) insitu.tg.sealevels.at(i+csi).at(k) -= mean;

        //Fill in airpressure vector... even though I might never use it...
        insitu.tg.airpressures.at(i+csi).resize(insitu.tg.sealevels.at(i+csi).size(),-66.6);

        cout<<"Station #"<<i+csi+1<<" ("<<insitu.tg.titles.at(i+csi)<<") has "<<insitu.tg.sealevels.at(i+csi).size()<<" sea level measurements."<<endl;
      }//End of if init != 1.
    }//End of loop through folders.
    cout<<"Finished loading PSMSL tide gauge data."<<endl;
  }//End of PSMSL loading.
  ////////////////////////////////////////////////////////////////////////
  //Load Abbas's tide gauge data.
  ////////////////////////////////////////////////////////////////////////
  if(insitu.loadABBAS==1){
    cout << "Loading Abbas's tide gauge data."<<endl;

    //Abbas's data will be entered into the * vectors at this index:
    csi = (long long)insitu.tg.titles.size();

    //Create list of abbreviations that are found in files in the tide gauge
    //data folder.  Each of these abbreviations stands for a particular tide
    //gauge station, so a more verbose title and coordinates are defined too.

    //65 30 N  37 00 W  AMMASSALIK
    stations.push_back("AMM");
    lowercase_stations.push_back("amm");
    insitu.tg.titles.push_back("Ammassalik");
    insitu.tg.grid.lat.push_back(65.0+30.0/60.0);
    insitu.tg.grid.lon.push_back(360.0 - 37.0);

    //60 43 N  46 02 W  QAQORTOQ                               =20
    stations.push_back("QAQ");
    lowercase_stations.push_back("qaq");
    insitu.tg.titles.push_back("Qaqortoq");
    insitu.tg.grid.lat.push_back(60.0+43.0/60.0);
    insitu.tg.grid.lon.push_back(360.0 - 46.0-2.0/60.0);
  //*/
    //64 10 N  51 44 W  NUU=NUUK=GODTHAB
    stations.push_back("NUU");
    lowercase_stations.push_back("nuu");
    insitu.tg.titles.push_back("Nuuk (Godthab)");
    insitu.tg.grid.lat.push_back(64.0+10.0/60.0);
    insitu.tg.grid.lon.push_back(360.0 - 51.0-44.0/60.0);

    //65 25 N  52 54 W  MANIITSOQ                              =20
    stations.push_back("MAN");
    lowercase_stations.push_back("man");
    insitu.tg.titles.push_back("Maniitsoq");
    insitu.tg.grid.lat.push_back(65.0+25.0/60.0);
    insitu.tg.grid.lon.push_back(360.0 - 52.0-54.0/60.0);

    //66 56 N  53 40 W  SISIMIUT                               =20
    stations.push_back("SIS");
    lowercase_stations.push_back("sis");
    insitu.tg.titles.push_back("Sisimiut");
    insitu.tg.grid.lat.push_back(66.0+56.0/60.0);
    insitu.tg.grid.lon.push_back(360.0 - 53.0-40.0/60.0);

    //68 43 N  52 53 W  AASIAAT                                =20
    stations.push_back("AAS");
    lowercase_stations.push_back("aas");
    insitu.tg.titles.push_back("Aasiaat");
    insitu.tg.grid.lat.push_back(68.0+43.0/60.0);
    insitu.tg.grid.lon.push_back(360.0 - 52.0-53.0/60.0);

    //69 13 N  51 06 W  ILULISSAT                              =20
    stations.push_back("ILU");
    lowercase_stations.push_back("ilu");
    insitu.tg.titles.push_back("Ilulissat");
    insitu.tg.grid.lat.push_back(69.0+13.0/60.0);
    insitu.tg.grid.lon.push_back(360.0 - 51.0-6.0/60.0);

    //Abbas says that "UMM=UMMANNAQ (70.670 N, 52.13W)", but abbrev is UUM.
    stations.push_back("UUM");
    lowercase_stations.push_back("uum");
    insitu.tg.titles.push_back("Ummannaq");
    insitu.tg.grid.lat.push_back(70.67);
    insitu.tg.grid.lon.push_back(360.0 - 52.13);
    //*/
    
    if(insitu.init_only != 1){

      //Find elevation of each station.
      for(i=csi;i<(long long)insitu.tg.grid.lat.size();i++) insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(i),insitu.tg.grid.lon.at(i),ancillary));

      //Initialize vectors.
      insitu.tg.times.resize(insitu.tg.titles.size());
      insitu.tg.sealevels.resize(insitu.tg.titles.size());
      insitu.tg.airpressures.resize(insitu.tg.titles.size());
      insitu.tg.salinities.resize(insitu.tg.titles.size());
      insitu.tg.fes_julian_times.resize(insitu.tg.titles.size());

      //Open the tide gauge data folder.
      basefolder = gaiafolder();
      basefolder.append(insitu.tg.basefolder);
      basefolder.append("Abbas_tide_gauge_data/");
      dataDIR = opendir(basefolder.c_str());

      //Search the tide gauge folder for files with extensions of .CHK - these
      //files contain the tide gauge data.  Files with .sqd extensions contain
      //surface air pressure data.
      dataDirEnt = readdir(dataDIR);
      while(dataDirEnt != NULL){
        temp_string = insitu.tg.basefolder;
        strcpy(s,dataDirEnt->d_name);
        temp_string.append(s);
        if(((int)temp_string.find(".CHK"))!=(int)string::npos){
          chk_filenames.push_back(s);
        }
        else if(((int)temp_string.find(".sqd"))!=(int)string::npos){
          sqd_filenames.push_back(s);
        }
        dataDirEnt = readdir(dataDIR);
      }//End of while loop through files

      //Close the tide gauge data folder.
      closedir(dataDIR);

      //Loop through station vector.  For each abbreviation contained therein,
      //loop through the chk_filenames vector and extract matching filenames.
      //Sort them so that they are listed in alphabetical order (which should
      //also sort them in chronological order) and read the tide gauge data
      //in those files.  Also, remove inverse barometer effect during loading.
      for(i=0;i<(long long)stations.size();i++){
        filecounter=0;
        station_filenames.clear();//Clear the station filenames vector.
        station_pressure_filenames.clear();
        k=0;//Reset counter of times read in for each station.
        previous_julian_year = -1;

        //Clear the character time vector- used to compare times obtained from
        //characters in the sealevel file to times obtained from characters in
        //the air pressure file (stored in pressure_time).
        character_time.clear();

        //Search for files that contain the current abbreviation.
        for(j=0;j<(long long)chk_filenames.size();j++){
          if(((int)chk_filenames.at(j).find(stations.at(i).c_str()))!=(int)string::npos) station_filenames.push_back(chk_filenames.at(j).c_str());
        }//End of loop through chk_filenames vector.

        //Sort station_filenames.
        sort(station_filenames.begin(),station_filenames.end());

        //Search for air pressure files that contains the current abbreviation.
        for(j=0;j<(long long)sqd_filenames.size();j++){
          if(((int)sqd_filenames.at(j).find(lowercase_stations.at(i).c_str()))!=(int)string::npos){
            station_pressure_filenames.push_back(sqd_filenames.at(j).c_str());
          }
        }//End of loop through sqd_filenames vector.

        //Sort station_pressure_filenames.
        sort(station_pressure_filenames.begin(),station_pressure_filenames.end());

        //Debug
        if(1==2){
          cout<<"Filenames for abbreviation "<<stations.at(i)<<":"<<endl;
          for(j=0;j<(long long)station_filenames.size();j++){
            cout<<station_filenames.at(j)<<endl;
          }
          for(j=0;j<(long long)station_pressure_filenames.size();j++){
            cout<<station_pressure_filenames.at(j)<<endl;
          }
        }

        //Loop through station_filenames, read data from each file.
        for(j=0;j<(long long)station_filenames.size();j++){
          basefolder=gaiafolder();
          basefolder.append(insitu.tg.basefolder);
          file = basefolder;
          file.append("Abbas_tide_gauge_data/");
          file.append(station_filenames.at(j));
          //cout<<"Loading tide gauge file "<<station_filenames.at(j)<<endl;
          //Open input file for reading.
          input_file.open(file.c_str());
          //Warn if file doesn't open correctly.
          if(input_file == NULL) cout<<"File failed to open:  "<<file<<endl;

          //Read each line of data as a string, parse into numeric values,
          //then add each number to its respective vector.  BUT- ignore the
          //line if it contains an "M", which Abbas says means "error".
          while(getline(input_file,line1)){// and filecounter < 20){
            filecounter++;
            if(line1.length() > 56) error = line1.substr(52,4);
            else error = "   M";
            //Only read this line if there isn't an "M" at this spot.
            if((pos=error.find("M"))==(int)string::npos){
              //Temperatures and salinities have been skipped for now.
              //stream1.str(line1.substr(88,24));
              //stream1 >> temp;
              //stream1.clear();
              //stream1.str(line1.substr(112,24));
              //stream1 >> salinity;
              //stream1.clear();

              //Read the Julian year.
              stream1.clear();
              stream1.str(line1.substr(8,4));
              stream1 >> julian_year;
              //cout<<"Julian year: "<<julian_year<<endl;

              //Read the Julian day (Abbas-relative to jan 1 of julian_year).
              //stream1.clear();
              //stream1.str(line1.substr(24,28));
              //stream1 >> time;
              //time--;//B/c Jan1 has julian_day=1! (should be 0, right?)
              //cout<<"Julian day: "<<fixed<<setprecision(10)<<setw(20)<<time<<endl;

              //Read the sea level measurement in mm.
              stream1.clear();
              stream1.str(line1.substr(56,32));
              stream1 >> sealevel;
              stream1.clear();

              //Tide gauge files are relative to midnight of jan 1
              //of the julian_year.  So each "time" (julian date in file)
              //needs to have this "epoch" added.  Only calculate the epoch
              //again if the current "julian_year" is different than the last.
              if(julian_year != previous_julian_year){

                //Convert 00:00 jan 1 "julian_year" to a julian date.
                time_conversion.calendar_time.front() = julian_year;
                time_conversion.calendar_time.at(1) = 1;
                time_conversion.calendar_time.at(2) = 1;
                time_conversion.calendar_time.at(3) = 0;
                time_conversion.calendar_time.at(4) = 0;
                time_conversion.calendar_time.at(5) = 0;
                cal2fes_jul(time_conversion);
                current_fes_julian_epoch = time_conversion.fes_jul_days;

                previous_julian_year = julian_year;
              }//End of (rare) computation of julian date of 0:0/1/1/julian_year.

              //Current time in julian days since 1950-1-1 0:0.0
              //current_fes_julian_date = time + current_fes_julian_epoch;

              //Extract time from character string, convert to FES Julian
              //date and UTC seconds past J2000.
              //cout<<"Converting date/time: "<<line1.substr(0,26)<<endl;
              stream1.clear();
              stream1.str(line1.substr(1,2));
              stream1 >> time_conversion.calendar_time.at(2);
              stream1.clear();
              //cout<<"Day: "<<time_conversion.calendar_time.at(2)<<endl;
              temp_string = line1.substr(4,3);
              if(temp_string == "JAN") time_conversion.calendar_time.at(1) = 1;
              else if(temp_string == "FEB") time_conversion.calendar_time.at(1) = 2;
              else if(temp_string == "MAR") time_conversion.calendar_time.at(1) = 3;
              else if(temp_string == "APR") time_conversion.calendar_time.at(1) = 4;
              else if(temp_string == "MAY") time_conversion.calendar_time.at(1) = 5;
              else if(temp_string == "JUN") time_conversion.calendar_time.at(1) = 6;
              else if(temp_string == "JUL") time_conversion.calendar_time.at(1) = 7;
              else if(temp_string == "AUG") time_conversion.calendar_time.at(1) = 8;
              else if(temp_string == "SEP") time_conversion.calendar_time.at(1) = 9;
              else if(temp_string == "OCT") time_conversion.calendar_time.at(1) = 10;
              else if(temp_string == "NOV") time_conversion.calendar_time.at(1) = 11;
              else if(temp_string == "DEC") time_conversion.calendar_time.at(1) = 12;
              //cout<<"Month: "<<temp_string<<" = "<<time_conversion.calendar_time.at(1)<<endl;
              stream1.clear();
              stream1.str(line1.substr(13,2));
              stream1 >> time_conversion.calendar_time.at(3);
              stream1.clear();
              stream1.str(line1.substr(16,2));
              stream1 >> time_conversion.calendar_time.at(4);
              stream1.clear();
              stream1.str(line1.substr(19,2));
              stream1 >> time_conversion.calendar_time.at(5);
              stream1.clear();
              //cout<<"Time: "<<time_conversion.calendar_time.at(3)<<":"<<time_conversion.calendar_time.at(4)<<":"<<time_conversion.calendar_time.at(5)<<endl;
              cal2utc(time_conversion);
              current_character_time = time_conversion.utc_seconds;

              //Convert to fes_jul here instead of using time+c_f_j_e b/c
              //my custom routines return more digits.
              cal2fes_jul(time_conversion);
              //temp_double = time_conversion.fes_jul_days;
              //cout<<"current_fes_julian_date: "<<fixed<<setprecision(10)<<setw(20)<<current_fes_julian_date<<" from char time: "<<fixed<<setprecision(10)<<setw(20)<<temp_double<<endl;

              //If this is the first time value from this station, add it
              //to times/sealevels unconditionally.
              if(k==0){
                insitu.tg.times.at(i+csi).push_back(current_character_time);
                insitu.tg.fes_julian_times.at(i+csi).push_back(time_conversion.fes_jul_days);
                character_time.push_back(current_character_time);
                insitu.tg.sealevels.at(i+csi).push_back(sealevel/10.0);//Convert mm sealevel to cm.
                k++;//Read another tide gauge measurement.
              }
              //Check that current time is larger than the most recent time value
              //stored in the times array for this station.  This is
              //because the tide gauge files contain overlapping data.
              else if(current_character_time > insitu.tg.times.at(i+csi).at(k-1)){
                insitu.tg.times.at(i+csi).push_back(current_character_time);
                insitu.tg.fes_julian_times.at(i+csi).push_back(time_conversion.fes_jul_days);
                character_time.push_back(current_character_time);
                insitu.tg.sealevels.at(i+csi).push_back(sealevel/10.0);//Convert mm sealevel to cm.
                k++;//Read another tide gauge measurement.
              }
              //else cout<<"Ignoring overlapping time at "<<(long long)temp_double<<" (mult times is already up to "<<insitu.tg.times.at(i+csi).at(k-1)<<")."<<endl;
            }//End of "if this line doesn't contain an error"
          }//End of loop through lines in the current file.
          input_file.close();//Close the current file.
        }//End of loop through station_filenames.
        cout<<"Station #"<<i+csi+1<<" ("<<insitu.tg.titles.at(i+csi)<<") has "<<insitu.tg.sealevels.at(i+csi).size()<<" sea level measurements."<<endl;

        //Skip air pressure loading if this is != 0.
        skip_air_pressures = 1;
        if(skip_air_pressures != 0){
          insitu.tg.airpressures.at(i+csi).resize(insitu.tg.sealevels.at(i+csi).size(),-66.6);
        }
        else{
        //Read atm pressure files- load air pressures in mbars (or hPa)- check
        //to make sure that this file contains times that are identical to those
        //in the sealevel files.
        pressure_time.clear();//Temp vector for times in air press files
        k=0;//Initialize counter of air pressure times here.
        for(l=0;l<(long long)station_pressure_filenames.size();l++){
          basefolder=gaiafolder();
          basefolder.append(insitu.tg.basefolder);
          file = basefolder;
          file.append(station_pressure_filenames.at(l));
          cout<<"Loading air pressure file "<<station_pressure_filenames.at(l)<<endl;
          //Open input file for reading.
          input_file.open(file.c_str());

          //Warn if file doesn't open correctly.
          if(input_file == NULL) cout<<"The air pressure file, "<<station_pressure_filenames.at(l)<< ", failed to open."<<endl;

          //Read each line of data as a string, parse into numeric values,
          //then add each number to its respective vector.  BUT- ignore the
          //line if it contains an "M", which Abbas says means "error".
          getline(input_file,line1);//Ignore header line.
          while(getline(input_file,line1) and k <(long long)insitu.tg.times.at(i+csi).size()){
            if(line1.length() > 46) error = line1.substr(42,4);
            else error = "   M";
            //cout<<"error: "<<error<<endl;
            //cout<<"line: "<<line1<<endl;
            //Only read this line if there isn't an "M" at this spot.
            if((pos=error.find("M"))==(int)string::npos){

              //cout<<line1.substr(0,25)<<endl;
              stream1.clear();
              stream1.str(line1.substr(25,17));
              stream1 >> airpressure;
              stream1.clear();

              //Convert character time to seconds past J2000.
              cout<<"CHANGE ME TO CUSTOM ROUTINE!"<<endl;
              //command = char2sec_command;
              //command.append(line1.substr(0,25));
              //conversion_output = popen(command.c_str(),"r");
              //f_return=fgets(s,max_length,conversion_output);
              //pclose(conversion_output);
              //sscanf(s,"%lf",&temp_double);
              current_character_time = (long long)temp_double;

              start_of_pressure_time_to_sealevel_time_comparison:

              //Only record this time if it's exactly the same as the time
              //obtained from the character time in the sealevel file.
              //cout<<"character_time.size() = "<<character_time.size()<<endl;
              if(current_character_time == character_time.at(k)){
                pressure_time.push_back((long long)temp_double);
                insitu.tg.airpressures.at(i+csi).push_back(airpressure);
                k++;
              }
              //Don't bother printing this statement- for some stations it
              //produces WAY too much output (apparently because the sealevel
              //files contain many more gaps than the air pressure files?)
              //else cout<<"current air pressure time (#"<<k<<"): "<<(long long)temp_double<<" but sealevel time is "<<insitu.tg.times.at(i+csi).at(k)+insitu.tg.epochs.at(i+csi)<<endl;
              //Instead, need to account for stations like Qaqortoq which have
              //no air pressure data for the first sealevel data points.  If the
              //current air pressure time is greater than current sealevel time
              //then I need to delete the time(s!) and sealevel entries.
              else if(current_character_time > character_time.at(k)){
                //Dec. cntr of sealevels read so mult_times index is right.
                if(k>0) k--;
                cout<<"Air pressure time "<<(long long)temp_double<<" is later than sealevel time(#"<<k<<"): "<<insitu.tg.times.at(i+csi).at(k)<<" so the sealevel time and sealevel value itself are being erased."<<endl;
                insitu.tg.times.at(i+csi).erase(insitu.tg.times.at(i+csi).begin()+k);
                insitu.tg.sealevels.at(i+csi).erase(insitu.tg.sealevels.at(i+csi).begin()+k);
                insitu.tg.fes_julian_times.at(i+csi).erase(insitu.tg.fes_julian_times.at(i+csi).begin()+k);
                character_time.erase(character_time.begin()+k);
                //cout<<"just erased points!  k-- = "<<k<<endl;
                //Now examine this air pressure time w.r.t. the new "k'th"
                //sealevel value.
                goto start_of_pressure_time_to_sealevel_time_comparison;
              }
              else{
                cout<<"Wow!  Something odd is happening!  vk9d2k94jf"<<endl;
                cout<<"character_time.size() = "<<character_time.size()<<endl;
                cout<<"k="<<k<<endl;
                cout<<"current_character_time: "<<current_character_time<<endl;
                cout<<"character_time.at(k): "<<character_time.at(k)<<endl;
              }
            }//End of "if this line doesn't contain an error" if statement.
          }//End of while loop through airpressure file lines.
          input_file.close();//Close the current file.
        }//End of loop through current station_pressure_filenames.

        //Make sure that the number of air pressure times matches the
        //number of sealevel times.
        if((long long)pressure_time.size() != (long long)insitu.tg.times.at(i+csi).size()){
          if((long long)pressure_time.size() < (long long)insitu.tg.times.at(i+csi).size()){
            cout<<"Number of air pressure values: "<<(long long)pressure_time.size()<<" is less than number of sealevel values: "<<(long long)insitu.tg.times.at(i+csi).size()<<endl;
            if(pressure_time.back() < insitu.tg.times.at(i+csi).back() and pressure_time.back() == insitu.tg.times.at(i+csi).at((long long)pressure_time.size()-1)){
              cout<<"This is because the last pressure time is "<<pressure_time.back()<<" but the last sealevel time is "<<insitu.tg.times.at(i+csi).back()<<" so I'm deleting the last elements of the sealevel and mult_times vectors."<<endl;
              insitu.tg.times.at(i+csi).erase(insitu.tg.times.at(i+csi).begin()+(long long)pressure_time.size(),insitu.tg.times.at(i+csi).end());
              insitu.tg.sealevels.at(i+csi).erase(insitu.tg.sealevels.at(i+csi).begin()+(long long)pressure_time.size(),insitu.tg.sealevels.at(i+csi).end());
            }
            else cout<<"WARNING!!! Something unexpected is happening!!! Trace me down in the code!"<<endl;
          }
          else if((long long)pressure_time.size() > (long long)insitu.tg.times.at(i+csi).size()){
            cout<<"Number of air pressure values: "<<(long long)pressure_time.size()<<" is greater than number of sealevel values: "<<(long long)insitu.tg.times.at(i+csi).size()<<endl;
            cout<<"HAVEN'T PROGRAMMED THIS CASE YET!!!!!"<<endl<<"!!!!!!!!!!!!!!!!!!!!!!!!!!!"<<endl<<endl;
          }
        }
        //Just to make sure I've fixed the problem:
        if((long long)pressure_time.size() != (long long)insitu.tg.times.at(i+csi).size()) cout<<"WARNING!! Still have a problem because number of air pressure values: "<<(long long)pressure_time.size()<<" STILL doesn't equal number of sealevel values: "<<(long long)insitu.tg.times.at(i+csi).size()<<endl;

        //Additional check:
        if((long long)insitu.tg.times.at(i+csi).size() != (long long)insitu.tg.sealevels.at(i+csi).size()) cout<<"WARNING!! Number of times at which sealevel was measured: "<<insitu.tg.times.at(i+csi).size()<<" doesn't equal number of sealevel values: "<<(long long)insitu.tg.sealevels.at(i+csi).size()<<endl;

        cout<<"Matched these sealevel measurements against a total of "<<insitu.tg.airpressures.at(i+csi).size()<<" air pressure measurements with which to remove the inverse barometer effect."<<endl;

        //Loop through all airpressure measurements and calculate the mean.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.airpressures.at(i+csi).size();k++){
          mean += insitu.tg.airpressures.at(i+csi).at(k);
        }
        mean /= (double)insitu.tg.airpressures.at(i+csi).size();
        cout<<"Mean atmospheric pressure: "<<mean<<" mbars."<<endl;

        //Remove inverse barometer effect.
        cout<<"insitu.tg.airpressures.at(i+csi).size() = "<<insitu.tg.airpressures.at(i+csi).size()<<endl;
        cout<<"insitu.tg.sealevels.at(i+csi).size() = "<<insitu.tg.sealevels.at(i+csi).size()<<endl;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi).size();k++){
          insitu.tg.sealevels.at(i+csi).at(k) += (insitu.tg.airpressures.at(i+csi).at(k) - mean)/1.005;
        }
        }//End of "to load or not to load air pressure" if statement.

        //Loop through all sealevel measurements and calculate the mean.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi).size();k++){
          mean += insitu.tg.sealevels.at(i+csi).at(k);
        }
        mean /= (double)insitu.tg.sealevels.at(i+csi).size();

        //Loop through all sealevel measurements and subtract the mean.
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi).size();k++){
          insitu.tg.sealevels.at(i+csi).at(k) -= mean;
        }

      }//End of loop through station vector.
    }
  }//End of Abbas loading code.
  ////////////////////////////////////////////////////////////////////////
  //Load Cecilia's tide gauge data.
  ////////////////////////////////////////////////////////////////////////
  if(insitu.loadCEC>0){
    cout << "Loading Cecilia's tide gauge data."<<endl;

    //Cecilia's data will be entered into the * vectors at this index:
    csi = (long long)insitu.tg.titles.size();

    //Cecilia's time is in "days of 2005" where 12:0:0 jan 1, 2005 is 1.5:
    time_conversion.calendar_time.resize(6,0);
    time_conversion.calendar_time.front() = 2004;//year
    time_conversion.calendar_time.at(1) = 12;//month - 0 is meaningless!
    time_conversion.calendar_time.at(2) = 31;//day - 0 is meaningless!
    time_conversion.calendar_time.at(3) = 0;//hours
    time_conversion.calendar_time.at(4) = 0;//minutes
    time_conversion.calendar_time.at(5) = 0;//seconds
    cal2utc(time_conversion);//Convert calendar time to UTC seconds.
    cecilia_utc_epoch = time_conversion.utc_seconds;
    cal2fes_jul(time_conversion);//Convert to FES Julian date.
    cecilia_fes_epoch = time_conversion.fes_jul_days;

    i=0;//This isn't an index- it's a counter- of the stations loaded HERE.
    //Cecilia's ts: 1 load only raw file; 2 also load detided file (second!)
    for(l=0;l<1;l++){
      i++;
      basefolder = gaiafolder();
      basefolder.append(insitu.tg.basefolder);
      basefolder.append("cecilia/");
      file = basefolder;
      if(l==0){
        file.append("ABPR1_raw.txt");
        insitu.tg.titles.push_back("ABPR1 raw");
      }
      else if(l==1){
        file.append("ABPR1_detided.txt");
        insitu.tg.titles.push_back("ABPR1 detided");
      }
      else cout<<"WARNING! SHOULD NEVER SEE THIS!!"<<endl;

      insitu.tg.grid.lat.push_back(89.254333333);
      insitu.tg.grid.lon.push_back(60.359666667);

      //Find elevation at this station.
      insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(csi+i-1),insitu.tg.grid.lon.at(csi+i-1),ancillary));
      
      if(insitu.init_only != 1){
        //Open input file for reading.
        input_file.open(file.c_str());
        //Warn if file doesn't open correctly.
        if(input_file == NULL) cout<<"Cecilia's file, "<< file << ", failed to open."<<endl;

        //Add another entry to each of * vectors.
        insitu.tg.sealevels.resize(csi+i);
        insitu.tg.airpressures.resize(csi+i);
        insitu.tg.times.resize(csi+i);
        insitu.tg.fes_julian_times.resize(csi+i);

        while(getline(input_file,line1)){
          //Read "day of 2005"
          temp_string = line1.substr(0,17);
          //cout<<"day of 2005: "<<temp_string<<".end."<<endl;
          stream1.clear();
          stream1.str(temp_string);
          stream1 >> temp_double;
          time_conversion.utc_seconds = (long long)(temp_double*86400.0);//Days to seconds.
          //Now convert from seconds since 0:0:0 Dec 31,2004.
          time_conversion.utc_seconds += cecilia_utc_epoch;
          //Save to (UTC) mult_times vector:
          insitu.tg.times.at(csi+i-1).push_back(time_conversion.utc_seconds);
          //Convert this UTC time to a FES julian date:
          utc2fes_jul(time_conversion);
          //Save to fes vector:
          insitu.tg.fes_julian_times.at(csi+i-1).push_back(time_conversion.fes_jul_days);

          //Read sealevel in mm:
          temp_string = line1.substr(17,17);
          //cout<<"sealevel mm: "<<temp_string<<".end."<<endl;
          stream1.clear();
          stream1.str(temp_string);
          stream1 >> temp_double;
          //cout<<"sealevel as a double: "<<temp_double<<endl;
          insitu.tg.sealevels.at(csi+i-1).push_back(temp_double/10.0);
        }//End of loop through file lines.

        //Air pressure vector will probably never be used, but fill it anyway!
        insitu.tg.airpressures.at(csi+i-1).resize(insitu.tg.times.at(csi+i-1).size());

        //Calculate and subtract the mean.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi-1).size();k++) mean += insitu.tg.sealevels.at(i+csi-1).at(k);
        mean /= (double)insitu.tg.sealevels.at(i+csi-1).size();
        cout<<"Mean (subtracted): "<<mean<<endl;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi-1).size();k++) insitu.tg.sealevels.at(i+csi-1).at(k) -= mean;

        cout<<"Station #"<<i+csi<<" ("<<insitu.tg.titles.at(i+csi-1)<<") has "<<insitu.tg.sealevels.at(i+csi-1).size()<<" sea level measurements."<<endl;
        input_file.close();//Close Cecilia's file.
      }
    }//End of for loop that loads files.

    //Load Cecilia's new data - ABPR1 and ABPR3 synthesized data, from t_tide's analysis
    //only for constituents with a SNR>4.
    
    //Cecilia's data will be entered into the * vectors at this index:
    csi = (long long)insitu.tg.titles.size();

    i=0;//This isn't an index- it's a counter- of the stations loaded HERE.
    //Cecilia's new ts: 1 load only ABPR1new, 2: load ABPR1new and ABPR3new.
    for(l=0;l<0;l++){
      i++;
      basefolder = gaiafolder();
      basefolder.append(insitu.tg.basefolder);
      basefolder.append("cecilia/");
      file = basefolder;
      if(l==0){
        file.append("ABPR1new.txt");
        insitu.tg.titles.push_back("ABPR1 pred");
        insitu.tg.grid.lat.push_back(89.254333333);
        insitu.tg.grid.lon.push_back(60.359666667);
      }
      else if(l==1){
        file.append("ABPR3new.txt");
        insitu.tg.titles.push_back("ABPR3 pred");
        insitu.tg.grid.lat.push_back(89.2475);
        insitu.tg.grid.lon.push_back(148.1257);
      }
      else cout<<"WARNING! SHOULD NEVER SEE THIS!!"<<endl;

      //Find elevation at this station.
      insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(csi+i-1),insitu.tg.grid.lon.at(csi+i-1),ancillary));
      
      if(insitu.init_only != 1){
        double cyear, cmonth, cday, chour, cminute, csecond, cheight;
        //Open input file for reading.
        in_fp = fopen(file.c_str(),"r");
        //Warn if file doesn't open correctly.
        if(in_fp == NULL) cout<<"Cecilia's file, "<< file << ", failed to open."<<endl;

        //Add another entry to each of * vectors.
        insitu.tg.sealevels.resize(csi+i);
        insitu.tg.airpressures.resize(csi+i);
        insitu.tg.times.resize(csi+i);
        insitu.tg.fes_julian_times.resize(csi+i);

        while(fgets(s,max_length,in_fp)!=NULL){
          sscanf(s,"%lf %lf %lf %lf %lf %lf %lf",&cyear, &cmonth, &cday, &chour, &cminute, &csecond, &cheight);
          time_conversion.calendar_time.front() = (long)cyear;//year
          time_conversion.calendar_time.at(1) = (long)cmonth;//month - 0 is meaningless!
          time_conversion.calendar_time.at(2) = (long)cday;//day - 0 is meaningless!
          time_conversion.calendar_time.at(3) = (long)chour;//hours
          time_conversion.calendar_time.at(4) = (long)cminute;//minutes
          time_conversion.calendar_time.at(5) = (long)csecond;//seconds
          cal2utc(time_conversion);//Convert calendar time to UTC seconds.
          //Save to (UTC) mult_times vector:
          insitu.tg.times.at(csi+i-1).push_back(time_conversion.utc_seconds);
          //Convert this UTC time to a FES julian date:
          utc2fes_jul(time_conversion);
          //Save to fes vector:
          insitu.tg.fes_julian_times.at(csi+i-1).push_back(time_conversion.fes_jul_days);

          insitu.tg.sealevels.at(csi+i-1).push_back(cheight);
        }//End of loop through file lines.

        //Air pressure vector will probably never be used, but fill it anyway!
        insitu.tg.airpressures.at(csi+i-1).resize(insitu.tg.times.at(csi+i-1).size());

        //Calculate and subtract the mean.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi-1).size();k++) mean += insitu.tg.sealevels.at(i+csi-1).at(k);
        mean /= (double)insitu.tg.sealevels.at(i+csi-1).size();
        cout<<"Mean (subtracted): "<<mean<<endl;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi-1).size();k++) insitu.tg.sealevels.at(i+csi-1).at(k) -= mean;

        cout<<"Station #"<<i+csi<<" ("<<insitu.tg.titles.at(i+csi-1)<<") has "<<insitu.tg.sealevels.at(i+csi-1).size()<<" sea level measurements."<<endl;
        fclose(in_fp);//Close Cecilia's file.
      }
    }//End of for loop that loads files.
    
    //Load Cecilia's newer data - ABPR1 and ABPR3 raw data, and Beaufort
    //Gyre data from BGEP project of Andrey Proshutinsky's at www.whoi.edu.
    
    //Cecilia's data will be entered into the * vectors at this index:
    csi = (long long)insitu.tg.titles.size();

    i=0;//This isn't an index- it's a counter- of the stations loaded HERE.
    //Cecilia's new ts: 1 ABPR1newer, 2: ABPR3newer, 3: BGEP data.
    for(l=0;l<0;l++){
      i++;
      basefolder = gaiafolder();
      basefolder.append(insitu.tg.basefolder);
      basefolder.append("cecilia/");
      file = basefolder;
      if(l==0){
        file.append("ABPRs1&3/abpr1.txt");
        insitu.tg.titles.push_back("ABPR1newer");
        insitu.tg.grid.lat.push_back(89.254333333);
        insitu.tg.grid.lon.push_back(60.359666667);
      }
      else if(l==1){
        file.append("ABPRs1&3/abpr3.txt");
        insitu.tg.titles.push_back("ABPR3newer");
        insitu.tg.grid.lat.push_back(89.2475);
        insitu.tg.grid.lon.push_back(148.1257);
      }
      else if(l==2){
        file.append("BPR_BGEP/BEAUFORT_BGEP.txt");
        insitu.tg.titles.push_back("BGEP");
        insitu.tg.grid.lat.push_back(75.007483);//Position approximate b/c it changed.
        insitu.tg.grid.lon.push_back(-150.0);//150deg W means -150 in my coords, right?
        
        //Cecilia's time is in "days of 2003" where 12:0:0 jan 1, 2003 is 1.5:
        time_conversion.calendar_time.resize(6,0);
        time_conversion.calendar_time.front() = 2002;//year
        time_conversion.calendar_time.at(1) = 12;//month - 0 is meaningless!
        time_conversion.calendar_time.at(2) = 31;//day - 0 is meaningless!
        time_conversion.calendar_time.at(3) = 0;//hours
        time_conversion.calendar_time.at(4) = 0;//minutes
        time_conversion.calendar_time.at(5) = 0;//seconds
        cal2utc(time_conversion);//Convert calendar time to UTC seconds.
        cecilia_utc_epoch = time_conversion.utc_seconds;
        cal2fes_jul(time_conversion);//Convert to FES Julian date.
        cecilia_fes_epoch = time_conversion.fes_jul_days;

      }
      else cout<<"WARNING! SHOULD NEVER SEE THIS!!"<<endl;

      //Find elevation at this station.
      insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(csi+i-1),insitu.tg.grid.lon.at(csi+i-1),ancillary));
      
      if(insitu.init_only != 1){
        //Open input file for reading.
        input_file.open(file.c_str());
        //Warn if file doesn't open correctly.
        if(input_file==NULL) cout<<"Cecilia's file, "<<file<< ", failed to open."<<endl;

        //Add another entry to each of * vectors.
        insitu.tg.sealevels.resize(csi+i);
        insitu.tg.airpressures.resize(csi+i);
        insitu.tg.times.resize(csi+i);
        insitu.tg.fes_julian_times.resize(csi+i);

        while(getline(input_file,line1)){
          //Read "day of 2003"
          temp_string = line1.substr(0,26);
          //cout<<"day of 2003: "<<temp_string<<endl;
          stream1.clear();
          stream1.str(temp_string);
          stream1 >> temp_double;
          time_conversion.utc_seconds = (long long)(temp_double*86400.0);//Days to seconds.
          //Now convert from seconds since 0:0:0 Dec 31,2004.
          time_conversion.utc_seconds += cecilia_utc_epoch;
          //Save to (UTC) mult_times vector:
          insitu.tg.times.at(csi+i-1).push_back(time_conversion.utc_seconds);
          //Convert this UTC time to a FES julian date:
          utc2fes_jul(time_conversion);
          //Save to fes vector:
          insitu.tg.fes_julian_times.at(csi+i-1).push_back(time_conversion.fes_jul_days);

          //Read sealevel in cm:
          temp_string = line1.substr(26,26);
          //cout<<"sealevel cm: "<<temp_string<<endl;
          stream1.clear();
          stream1.str(temp_string);
          stream1 >> temp_double;
          //cout<<"sealevel as a double: "<<temp_double<<endl;
          insitu.tg.sealevels.at(csi+i-1).push_back(temp_double);
        }//End of loop through file lines.

        //Air pressure vector will probably never be used, but fill it anyway!
        insitu.tg.airpressures.at(csi+i-1).resize(insitu.tg.times.at(csi+i-1).size());

        //Calculate and subtract the mean.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi-1).size();k++) mean += insitu.tg.sealevels.at(i+csi-1).at(k);
        mean /= (double)insitu.tg.sealevels.at(i+csi-1).size();
        cout<<"Mean (subtracted): "<<mean<<endl;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi-1).size();k++) insitu.tg.sealevels.at(i+csi-1).at(k) -= mean;

        cout<<"Station #"<<i+csi<<" ("<<insitu.tg.titles.at(i+csi-1)<<") has "<<insitu.tg.sealevels.at(i+csi-1).size()<<" sea level measurements."<<endl;
        input_file.close();//Close Cecilia's file.
      }
    }//End of for loop that loads files.
    cout<<"Finished loading Cecilia's tide gauge data."<<endl;
  }//End of Cecilia's data loading.
  ////////////////////////////////////////////////////////////////////////
  //Load tide gauge data from Canada
  ////////////////////////////////////////////////////////////////////////
  if(insitu.loadCanada==1){
    cout<<"Loading Canada's tide gauge data."<<endl;
    
    //Canada's data will be entered into the * vectors at this index:
    csi = (long long)insitu.tg.titles.size();
    
    //Build a list of files in the folder.
    basefolder = gaiafolder();
    basefolder.append(insitu.tg.basefolder);
    basefolder.append("Canadian_tide_gauges/");
    dataDIR = opendir(basefolder.c_str());
    file_list.clear();
    dataDirEnt = readdir(dataDIR);
    while(dataDirEnt != NULL){
      strcpy(s,dataDirEnt->d_name);
      temp_string = s;
      if((int)temp_string.length() > 2) file_list.push_back(s);
      dataDirEnt = readdir(dataDIR);
    }//End of while loop through files
    closedir(dataDIR);

    //Sort the files alphabetically.
    sort(file_list.begin(),file_list.end());

    //debug.
    if(1==2){
      cout<<"Files are: "<<endl;
      for(j=0;j<(long long)file_list.size();j++) cout<<file_list.at(j)<<endl;
      cout<<endl;
    }

    i=0;//This isn't an index- it's a counter- of the stations loaded HERE.
    //Read data from each file.
    for(j=0;j<(long long)file_list.size();j++){
      i++;
      file = basefolder;
      file.append(file_list.at(j));
      
      //Open input file for reading.
      in_fp = fopen(file.c_str(),"r");
      //Warn if file doesn't open correctly.
      if(in_fp == NULL) cout<<"Canadian file, "<< file << ", failed to open."<<endl;
     
      //Read line with station name.
      f_return=fgets(s,max_length,in_fp);
      token = strtok(s, ",");//read literal "Station_Name,"
      token = strtok(NULL, "\n");//Read station name.
      token.erase((int)token.size()-1,1);//Bad character!
      cout<<"Loading data from Canadian station: "<<token<<endl;
      insitu.tg.titles.push_back(token);
      
      //Ignore line with station number.
      f_return=fgets(s,max_length,in_fp);
    
      //Read line with station lat.
      f_return=fgets(s,max_length,in_fp);
      token = strtok(s, ",");
      token = strtok(NULL, "\n");
      sscanf(token.c_str(),"%lf", &temp_double);
      insitu.tg.grid.lat.push_back(temp_double);
      
      //Read line with station lon.
      f_return=fgets(s,max_length,in_fp);
      token = strtok(s, ",");
      token = strtok(NULL, "\n");
      sscanf(token.c_str(),"%lf", &temp_double);
      insitu.tg.grid.lon.push_back(-temp_double);//File's lon is reverse of mine.
      
      //Ignore line.
      f_return=fgets(s,max_length,in_fp);
    
      //Ignore line.
      f_return=fgets(s,max_length,in_fp);
    
      //Ignore line.
      f_return=fgets(s,max_length,in_fp);
    
      //Ignore line.
      f_return=fgets(s,max_length,in_fp);
    

      //Find elevation at this station.
      insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(csi+i-1),insitu.tg.grid.lon.at(csi+i-1),ancillary));
      
      if(insitu.init_only != 1){
        long cyear, cmonth, cday, chour, cminute;
        double cheight;

        //Add another entry to each of * vectors.
        insitu.tg.sealevels.resize(csi+i);
        insitu.tg.airpressures.resize(csi+i);
        insitu.tg.times.resize(csi+i);
        insitu.tg.fes_julian_times.resize(csi+i);

        while(fgets(s,max_length,in_fp)!=NULL){
          sscanf(s,"%ld/%ld/%ld %ld:%ld,%lf",&cyear, &cmonth, &cday, &chour, &cminute, &cheight);
          time_conversion.calendar_time.front() = (long)cyear;//year
          time_conversion.calendar_time.at(1) = (long)cmonth;//month - 0 is meaningless!
          time_conversion.calendar_time.at(2) = (long)cday;//day - 0 is meaningless!
          time_conversion.calendar_time.at(3) = (long)chour;//hours
          time_conversion.calendar_time.at(4) = (long)cminute;//minutes
          time_conversion.calendar_time.at(5) = 0;//seconds
          cal2utc(time_conversion);//Convert calendar time to UTC seconds.
          //Save to (UTC) mult_times vector:
          insitu.tg.times.at(csi+i-1).push_back(time_conversion.utc_seconds);
          //Convert this UTC time to a FES julian date:
          utc2fes_jul(time_conversion);
          //Save to fes vector:
          insitu.tg.fes_julian_times.at(csi+i-1).push_back(time_conversion.fes_jul_days);
          //Convert from m to cm.
          insitu.tg.sealevels.at(csi+i-1).push_back(cheight*100);
        }//End of loop through file lines.

        //Air pressure vector will probably never be used, but fill it anyway!
        insitu.tg.airpressures.at(csi+i-1).resize(insitu.tg.times.at(csi+i-1).size());

        //Calculate and subtract the mean.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi-1).size();k++) mean += insitu.tg.sealevels.at(i+csi-1).at(k);
        mean /= (double)insitu.tg.sealevels.at(i+csi-1).size();
        cout<<"Mean (subtracted): "<<mean<<endl;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi-1).size();k++) insitu.tg.sealevels.at(i+csi-1).at(k) -= mean;

        cout<<"Station #"<<i+csi<<" ("<<insitu.tg.titles.at(i+csi-1)<<") has "<<insitu.tg.sealevels.at(i+csi-1).size()<<" sea level measurements."<<endl;
        fclose(in_fp);//Close file.
      }
    }//End of for loop that loads files.
    cout<<"Finished loading Canada's tide gauge data."<<endl;
  }
  ////////////////////////////////////////////////////////////////////////
  //Load tide gauge data from DART
  ////////////////////////////////////////////////////////////////////////
  if(insitu.loadDART==1){
    cout<<"Loading DART tide gauge data."<<endl;
    
    //DART data will be entered into the * vectors at this index:
    csi = (long long)insitu.tg.titles.size();
    
    //Build a list of files in the folder.
    basefolder = gaiafolder();
    basefolder.append(insitu.tg.basefolder);
    basefolder.append("DART/");
    dataDIR = opendir(basefolder.c_str());
    file_list.clear();
    dataDirEnt = readdir(dataDIR);
    while(dataDirEnt != NULL){
      strcpy(s,dataDirEnt->d_name);
      temp_string = s;
      if((int)temp_string.length() > 2) file_list.push_back(s);
      dataDirEnt = readdir(dataDIR);
    }//End of while loop through files
    closedir(dataDIR);

    //Sort the files alphabetically.
    sort(file_list.begin(),file_list.end());

    //debug.
    if(1==1){
      cout<<"Files are: "<<endl;
      for(j=0;j<(long long)file_list.size();j++) cout<<file_list.at(j)<<endl;
      cout<<endl;
    }

    i=0;//This isn't an index- it's a counter- of the stations loaded HERE.
    //Read data from each file.
    for(j=0;j<(long long)file_list.size();j++){
      i++;
      file = basefolder;
      file.append(file_list.at(j));
      
      //Open input file for reading.
      in_fp = fopen(file.c_str(),"r");
      //Warn if file doesn't open correctly.
      if(in_fp == NULL) cout<<"DART file, "<< file << ", failed to open."<<endl;
     
      //Read line with station name.
      f_return=fgets(s,max_length,in_fp);
      token = strtok(s, " ");//Read station name.
      cout<<"Loading data from DART station: "<<token<<endl;
      insitu.tg.titles.push_back(token);
     
      double slat, slon;
      //If name matches a known station, set latitude and longitude manually!
      if(strcmp(token.c_str(),"d157_2003")==0){ slat = 52.6504; slon = -156.9405; }
      else if(strcmp(token.c_str(),"AK10")==0){ slat = 54.2857; slon = -158.5198; }
      else cout<<"WARNING!! DART Station name "<<token<<" was not recognized!!"<<endl;

      insitu.tg.grid.lat.push_back(slat);
      insitu.tg.grid.lon.push_back(slon);
      
      //Ignore lines.
      for(int tct=0;tct<12;tct++) f_return=fgets(s,max_length,in_fp);
    
      //Find elevation at this station.
      insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(csi+i-1),insitu.tg.grid.lon.at(csi+i-1),ancillary));
      
      if(insitu.init_only != 1){
        long cyear, cmonth, cday, chour, cminute;
        double csecond, cpressure;

        //Add another entry to each of * vectors.
        insitu.tg.sealevels.resize(csi+i);
        insitu.tg.airpressures.resize(csi+i);
        insitu.tg.times.resize(csi+i);
        insitu.tg.fes_julian_times.resize(csi+i);

        while(fgets(s,max_length,in_fp)!=NULL){
          sscanf(s,"%ld.%ld.%ld %ld:%ld:%lf %lf",&cyear, &cmonth, &cday, &chour, &cminute, &csecond, &cpressure);
          time_conversion.calendar_time.front() = (long)cyear;//year
          time_conversion.calendar_time.at(1) = (long)cmonth;//month - 0 is meaningless!
          time_conversion.calendar_time.at(2) = (long)cday;//day - 0 is meaningless!
          time_conversion.calendar_time.at(3) = (long)chour;//hours
          time_conversion.calendar_time.at(4) = (long)cminute;//minutes
          time_conversion.calendar_time.at(5) = (long)csecond;//seconds
          cal2utc(time_conversion);//Convert calendar time to UTC seconds.
          //Save to (UTC) mult_times vector:
          insitu.tg.times.at(csi+i-1).push_back(time_conversion.utc_seconds);
          //Convert this UTC time to a FES julian date:
          utc2fes_jul(time_conversion);
          //Save to fes vector:
          insitu.tg.fes_julian_times.at(csi+i-1).push_back(time_conversion.fes_jul_days);
          //The psi2cm conversion factor was calculated in definitions.hpp.
          insitu.tg.sealevels.at(csi+i-1).push_back(cpressure*psi2cm);
        }//End of loop through file lines.

        //Air pressure vector will probably never be used, but fill it anyway!
        insitu.tg.airpressures.at(csi+i-1).resize(insitu.tg.times.at(csi+i-1).size());

        //Calculate and subtract the mean.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi-1).size();k++) mean += insitu.tg.sealevels.at(i+csi-1).at(k);
        mean /= (double)insitu.tg.sealevels.at(i+csi-1).size();
        cout<<"Mean (subtracted): "<<mean<<endl;
        for(k=0;k<(long long)insitu.tg.sealevels.at(i+csi-1).size();k++) insitu.tg.sealevels.at(i+csi-1).at(k) -= mean;

        cout<<"Station #"<<i+csi<<" ("<<insitu.tg.titles.at(i+csi-1)<<") has "<<insitu.tg.sealevels.at(i+csi-1).size()<<" sea level measurements."<<endl;
        fclose(in_fp);//Close file.
      }
    }//End of for loop that loads files.
    cout<<"Finished loading DART data."<<endl;
  }
  ////////////////////////////////////////////////////////////////////////
  //Load BPR locations from Jamie Morison in the Fram Strait.
  ////////////////////////////////////////////////////////////////////////
  if(insitu.loadFRAM==1){
    cout << "Loading Jamie's FRAM Strait proposed tide gauge locations."<<endl;
    //Fram data will be entered into the * vectors at this index:
    csi = (long long)insitu.tg.titles.size();

    //Germans have a BPR at 78d 50, 2d 50' E, some I am not sure 78d 50' N, 3d W 
    insitu.tg.titles.push_back("German BPR");
    insitu.tg.grid.lat.push_back(78.0+50.0/60.0);
    insitu.tg.grid.lon.push_back(2.0+50.0/60.0);

    //Proposed new BPR at 78d 50' N, 3d W 
    insitu.tg.titles.push_back("Proposed BPR");
    insitu.tg.grid.lat.push_back(78.0+50.0/60.0);
    insitu.tg.grid.lon.push_back(360.0 - 3.0);
    
    //Add enough entries to * vectors to fit data from each folder.
    if(insitu.init_only != 1){
      //Create synth time series.
      insitu_s template_insitu;
      template_insitu.synth_interval = 3600;//Between data points, in seconds.
      template_insitu.num_synth_points = 24*365*2;
      template_insitu.synth_offset=0;//In seconds from J2000.
      create_synth_timeseries(template_insitu);
      //Initialize vectors.
      insitu.tg.times.resize(insitu.tg.titles.size());
      insitu.tg.sealevels.resize(insitu.tg.titles.size());
      insitu.tg.airpressures.resize(insitu.tg.titles.size());
      insitu.tg.salinities.resize(insitu.tg.titles.size());
      insitu.tg.fes_julian_times.resize(insitu.tg.titles.size());
     
      //Loop through these stations to fake time series data and elevations.
      for(i=csi;i<(long long)insitu.tg.grid.lat.size();i++){
        insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(i),insitu.tg.grid.lon.at(i),ancillary));
        insitu.tg.times.at(i) = template_insitu.tg.times.front();
        insitu.tg.fes_julian_times.at(i) = template_insitu.tg.fes_julian_times.front();
        insitu.tg.sealevels.at(i).resize(template_insitu.tg.times.front().size());
      }
    }
    cout<<"Finished loading proposed FRAM BPR locations."<<endl;
  }//End of FRAM loading.
  ////////////////////////////////////////////////////////////////////////
  //REMEMBER TO PUT THIS LAST TO FIX OTHER GAUGES NOT FILLING IN THE AMP/
  //PHASE VECTORS!
  //Load tide gauge data in Amp/Phase (AP) format.
  ////////////////////////////////////////////////////////////////////////
  if(insitu.loadAP==1){
    cout<<"Loading amp/phase tide gauge data."<<endl;

    //AP data will be entered into the * vectors at this index:
    csi = (long long)insitu.tg.titles.size();

    //Load generic amps/phase data and Proshutinsky's data.
    load_amps_n_phases(insitu,csi);
    load_proshutinsky(insitu,csi);

    cout<<"Finished loading amp/phase tide gauge data."<<endl;
  }//End of AP loading.
  else{//Need to resize amp/phase vectors anyway.
    if(insitu.init_only != 1){
      insitu.tg.amplitudes.resize(csi+i);
      insitu.tg.phases.resize(csi+i);
      insitu.tg.tide_booleans.resize(csi+i);
      insitu.tg.period_titles.resize(csi+i);
      insitu.tg.periods.resize(csi+i);
    }
  }
  
  //Compare location vectors from insitu_initialize() to ones created here.
  if(insitu.init_only != 1){
    if(init_lat.size() != insitu.tg.grid.lat.size()) cout<<"!!WARNING!! init_lat.size() = "<<init_lat.size()<<" while insitu.tg.grid.lat.size() = "<<insitu.tg.grid.lat.size()<<endl;
    if(init_lon.size() != insitu.tg.grid.lon.size()) cout<<"!!WARNING!! init_lon.size() = "<<init_lon.size()<<" while insitu.tg.grid.lon.size() = "<<insitu.tg.grid.lon.size()<<endl;
    //Print elements in case of a problem.
    //Assume lat and lon (of each type) have the same sizes.
    if(init_lat.size() > insitu.tg.grid.lat.size()){
      for(k=0;k<(long long)init_lat.size();k++){
        if(k<(long long)insitu.tg.grid.lat.size()){
          cout<<"!!WARNING!! init_lat.at("<<k<<") = "<<init_lat.at(k)<<" while insitu.tg.grid.lat.at("<<k<<") = "<<insitu.tg.grid.lat.at(k)<<endl;
          cout<<"!!WARNING!! init_lon.at("<<k<<") = "<<init_lon.at(k)<<" while insitu.tg.grid.lon.at("<<k<<") = "<<insitu.tg.grid.lon.at(k)<<endl;
        }
        else{
          cout<<"!!WARNING!! init_lat.at("<<k<<") = "<<init_lat.at(k)<<endl;
          cout<<"!!WARNING!! init_lon.at("<<k<<") = "<<init_lon.at(k)<<endl;
        }
      }
    }
    else if(init_lat.size() < insitu.tg.grid.lat.size()){
      for(k=0;k<(long long)insitu.tg.grid.lat.size();k++){
        if(k<(long long)init_lat.size()){
          cout<<"!!WARNING!! init_lat.at("<<k<<") = "<<init_lat.at(k)<<" while insitu.tg.grid.lat.at("<<k<<") = "<<insitu.tg.grid.lat.at(k)<<endl;
          cout<<"!!WARNING!! init_lon.at("<<k<<") = "<<init_lon.at(k)<<" while insitu.tg.grid.lon.at("<<k<<") = "<<insitu.tg.grid.lon.at(k)<<endl;
        }
        else{
          cout<<"!!WARNING!! insitu.tg.grid.lat.at("<<k<<") = "<<insitu.tg.grid.lat.at(k)<<endl;
          cout<<"!!WARNING!! insitu.tg.grid.lon.at("<<k<<") = "<<insitu.tg.grid.lon.at(k)<<endl;
        }
      }
    }
    else for(k=0;k<(long long)insitu.tg.grid.lat.size();k++){
      if(init_lat.at(k) != insitu.tg.grid.lat.at(k)) cout<<"!!WARNING!! init_lat.at("<<k<<") = "<<init_lat.at(k)<<" while insitu.tg.grid.lat.at("<<k<<") = "<<insitu.tg.grid.lat.at(k)<<endl;
      if(init_lon.at(k) != insitu.tg.grid.lon.at(k)) cout<<"!!WARNING!! init_lon.at("<<k<<") = "<<init_lon.at(k)<<" while insitu.tg.grid.lon.at("<<k<<") = "<<insitu.tg.grid.lon.at(k)<<endl;
    }
  }

  if(insitu.init_only != 1) cout << "Finished loading all tide gauge data."<<endl;
}

void load_amps_n_phases(insitu_s &insitu, long long &csi){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          csi - current index in insitu.tg vectors.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  double temp_double;
  string basefolder,file, temp_string;
  int last_station;//Used when loading Amp/Phase data.
  int keep_going;//same here.
  period_info_s period_info;
  ifstream input_file;//Used to grab strings from the file.
  istringstream stream1;//Used to grab data from the strings.
  string line1;
  basefolder = gaiafolder();
  basefolder.append(insitu.tg.basefolder);
  file = basefolder;
  file.append("amps_n_phases.txt");
  cout<<"Loading AP file "<<file<<endl;
  //Open input file for reading.
  input_file.open(file.c_str());
  //Warn if file doesn't open correctly.
  if(input_file == NULL) cout<<"The AP file, "<< file << ", failed to open."<<endl;

  //Ignore first header line.
  getline(input_file,line1);

  last_station=0;
  i=0;
  while(last_station == 0){
    i++;
    //Read blank header line.
    getline(input_file,line1);

    //Read station name and save it.
    getline(input_file,line1);
    cout<<"Loading AP data from station: "<<line1<<endl;
    insitu.tg.titles.push_back(line1);

    //Ignore header line.
    getline(input_file,line1);

    //Read latitude.
    getline(input_file,line1);
    stream1.clear();
    stream1.str(line1);
    stream1 >> temp_double;
    //Save latitude to tg.grid.lat
    insitu.tg.grid.lat.push_back(temp_double);
    //Need the "-1" because i is already 1 in the first loop.
    //cout<<"Latitude: "<<insitu.tg.grid.lat.at(csi+i-1)<<endl;

    //Read longitude.
    getline(input_file,line1);
    stream1.clear();
    stream1.str(line1);
    stream1 >> temp_double;
    //Save longitude to tg.grid.lon
    insitu.tg.grid.lon.push_back(temp_double);
    //cout<<"Longitude: "<<insitu.tg.grid.lon.at(csi+i-1)<<endl;
    
    //Find elevation at this station.
    insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(csi+i-1),insitu.tg.grid.lon.at(csi+i-1),ancillary));
    
    if(insitu.init_only != 1){
      //Add another entry to each of * vectors.
      insitu.tg.sealevels.resize(csi+i);
      insitu.tg.airpressures.resize(csi+i);
      insitu.tg.times.resize(csi+i);
      insitu.tg.fes_julian_times.resize(csi+i);
      //Do the same for the new amp/phase vectors.
      insitu.tg.amplitudes.resize(csi+i);
      insitu.tg.phases.resize(csi+i);
      insitu.tg.tide_booleans.resize(csi+i);
      insitu.tg.period_titles.resize(csi+i);
      insitu.tg.periods.resize(csi+i);
    }

    //Ignore amp units line and labels header line.
    getline(input_file,line1); getline(input_file,line1);

    //Loop through all amplitudes recorded for this station.
    keep_going = 1;
    while(keep_going == 1){
      if(getline(input_file,line1)){
        keep_going = 1;
      }
      else{
        cout<<"!!WARNING! problem reading amp_n_phase!"<<endl;
        keep_going = 0;
        last_station=1;
      }
      if(((int)line1.find("!!NEW TIDE GAUGE AMPS AND PHASES!!"))!=(int)string::npos){
        keep_going = 0;
      }
      else if(line1=="END OF FILE"){
        keep_going = 0;
        last_station = 1;
      }
      else{
        //cout<<"title,amp,phase,etc: "<<line1<<endl;
        //Read title.
        temp_string = line1.substr(1,4);
        //cout<<"title: "<<temp_string<<".end."<<endl;
        //Read frequency (in hours^(-1)?)
        stream1.clear();
        //cout<<"frequency: "<<line1.substr(6,9)<<endl;
        stream1.str(line1.substr(6,9));
        stream1 >> temp_double;
        //Convert to period, in seconds.
        temp_double = 1.0/temp_double*3600.0;
        is_this_period_recognized(temp_double,period_info);
        if(1==2){
          cout<<"p_i.title : "<<period_info.title<<endl;
          cout<<"p_i.period: "<<period_info.period<<endl;
          cout<<"period from file: "<<temp_double<<endl;
        }
        if(insitu.init_only != 1) insitu.tg.periods.at(csi+i-1).push_back(period_info.period);
   
        //Compare title in line1 to title from period_info.
        //if(temp_string != period_info.title) cout<<"title from line1: "<<temp_string<<" != (p_i.title) "<<period_info.title<<endl;

        //Save title from line1.
        if(insitu.init_only != 1) insitu.tg.period_titles.at(csi+i-1).push_back(temp_string);

        //Record whether or not this is a tide.
        if(insitu.init_only != 1) insitu.tg.tide_booleans.at(csi+i-1).push_back(period_info.tide);

        //Read amplitude (convert units from mm to cm unless otherwise specified)
        stream1.clear();
        temp_string = line1.substr(16,9);
        //cout<<"amp: "<<temp_string<<".end."<<endl;
        stream1.str(temp_string);
        stream1 >> temp_double;
        if(insitu.init_only != 1){
          //Cecilia's new AP data is in cm, not mm...
          double conversion = 1.0;
          if(((int)insitu.tg.titles.at(csi+i-1).find("ABPR1new"))!=(int)string::npos or ((int)insitu.tg.titles.at(csi+i-1).find("ABPR3new"))!=(int)string::npos) conversion = 1.0; 
          else conversion = 10.0;
          insitu.tg.amplitudes.at(csi+i-1).push_back(temp_double/conversion);
        }

        //Read phase (this could be VERY complicated- depends on epoch
        //and whether or not the semidiurnal tides are at 90 degrees by
        //convention (or whatever the deal is).
        stream1.clear();
        temp_string = line1.substr(37,6);
        //cout<<"phase: "<<temp_string<<endl;
        stream1.str(temp_string);
        stream1 >> temp_double;
        if(insitu.init_only != 1) insitu.tg.phases.at(csi+i-1).push_back(temp_double);

        //Eventually, I want to read in the errors and signal-noise ratio

      }//Else if it's just a regular amp/phase line.
    }//End of loop through this station's amplitudes/phases.
    if(insitu.init_only != 1){
      adjust_tg_phases(insitu,csi+i-1);

      generate_tg_timeseries(insitu,csi+i-1);
      
      cout<<"Station #"<<i+csi<<" ("<<insitu.tg.titles.at(csi+i-1)<<") has "<<insitu.tg.amplitudes.at(csi+i-1).size()<<" amplitudes/phases."<<endl;
    }
  }//End of while not finished with stations in this file.
  input_file.close();//Close the amps_n_phase file.
  
  //Update this passed-by-ref variable so next amp/phase file loads correctly.
  csi = (long long)insitu.tg.titles.size();
}

void load_proshutinsky(insitu_s &insitu, long long &csi){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          csi - current index in insitu.tg vectors.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  string basefolder,file, temp_string;
  period_info_s period_info;
  FILE *in_fp;
  char s[max_length];
  char* f_return;//Used to stop fgets return value warning.
  int latdeg, latmin, londeg, lonmin;
  float ptimezone, m2amp, m2phase, s2amp, s2phase, k1amp, k1phase, o1amp, o1phase;
  char westeast;
  
  basefolder = gaiafolder();
  basefolder.append(insitu.tg.basefolder);
  basefolder.append("Proshutinsky_data/new_data/");
  file = basefolder;
  file.append("DATA.BASE");
  cout<<"Loading Proshutinsky's file "<<file<<endl;
  in_fp = fopen(file.c_str(),"r");
  //Warn if file doesn't open correctly.
  if(in_fp == NULL) cout<<"Proshutinsky's file, "<< file << ", failed to open."<<endl;

  //Ignore header lines.
  int headerlines = 29;
  for(i=0;i<headerlines;i++) f_return=fgets(s,max_length,in_fp);

  //Read data for each station until end of file.
  i=0;
  while(fgets(s,max_length,in_fp)!=NULL){
    i++;
    
    temp_string = s;
    temp_string.erase(0,5);
    //Remove the \n at the end of the line.
    int pos;
    if((pos=temp_string.find("\n"))!=(int)string::npos){
      temp_string.swap(temp_string.erase(pos,2));
    }
    cout<<"Loading Proshutinsky's data from station: "<<temp_string<<endl;
    insitu.tg.titles.push_back(temp_string);

    //Now load line w/ location, time zone, amps, phases for M2, S2, K1, O1.
    f_return=fgets(s,max_length,in_fp);
    sscanf(s,"%d %d %d %d %c %f %f %f %f %f %f %f %f %f",&latdeg, &latmin, &londeg, &lonmin, &westeast, &ptimezone, &m2amp, &m2phase, &s2amp, &s2phase, &k1amp, &k1phase, &o1amp, &o1phase);
    //Save latitude to tg.grid.lat
    insitu.tg.grid.lat.push_back((double)latmin/60.0+(double)latdeg);
    //Need the "-1" because i is already 1 in the first loop.
    //cout<<"Latitude: "<<insitu.tg.grid.lat.at(csi+i-1)<<endl;

    //Save longitude to tg.grid.lon
    double temp_lon = (double)lonmin/60.0+(double)londeg;
    if(westeast == 'W') insitu.tg.grid.lon.push_back(360.0 - temp_lon);
    else if(westeast == 'E') insitu.tg.grid.lon.push_back(temp_lon);
    //cout<<"westeast: "<<westeast<<endl;
    //cout<<"Longitude: "<<insitu.tg.grid.lon.at(csi+i-1)<<endl;
    
    //Find elevation at this station.
    insitu.tg.grid.elev.push_back(0);//nearest_topo_pt_elev(insitu.tg.grid.lat.at(csi+i-1),insitu.tg.grid.lon.at(csi+i-1),ancillary));
    
    if(insitu.init_only != 1){
  
      /*Adjust phase by timezone (in hours away from Greenwich).
      m2phase -= ptimezone*360.0/(m2_period/3600.0);//hours times deg/hours
      s2phase -= ptimezone*360.0/(s2_period/3600.0);//hours times deg/hours
      k1phase -= ptimezone*360.0/(k1_period/3600.0);//hours times deg/hours
      o1phase -= ptimezone*360.0/(o1_period/3600.0);//hours times deg/hours */

      //Add another entry to each of * vectors.
      insitu.tg.sealevels.resize(csi+i);
      insitu.tg.airpressures.resize(csi+i);
      insitu.tg.times.resize(csi+i);
      insitu.tg.fes_julian_times.resize(csi+i);
      //Do the same for the new amp/phase vectors.
      insitu.tg.amplitudes.resize(csi+i);
      insitu.tg.phases.resize(csi+i);
      insitu.tg.tide_booleans.resize(csi+i);
      insitu.tg.period_titles.resize(csi+i);
      insitu.tg.periods.resize(csi+i);
     
      if(fabs(m2amp- 777.7) > high_tolerance){
        insitu.tg.periods.at(csi+i-1).push_back(m2_period);
        insitu.tg.period_titles.at(csi+i-1).push_back("M2");
        insitu.tg.tide_booleans.at(csi+i-1).push_back(1);
        insitu.tg.amplitudes.at(csi+i-1).push_back(m2amp);
        insitu.tg.phases.at(csi+i-1).push_back(m2phase);
      }

      if(fabs(s2amp- 777.7) > high_tolerance){
        insitu.tg.periods.at(csi+i-1).push_back(s2_period);
        insitu.tg.period_titles.at(csi+i-1).push_back("S2");
        insitu.tg.tide_booleans.at(csi+i-1).push_back(1);
        insitu.tg.amplitudes.at(csi+i-1).push_back(s2amp);
        insitu.tg.phases.at(csi+i-1).push_back(s2phase);
      }

      if(fabs(k1amp- 777.7) > high_tolerance){
        insitu.tg.periods.at(csi+i-1).push_back(k1_period);
        insitu.tg.period_titles.at(csi+i-1).push_back("K1");
        insitu.tg.tide_booleans.at(csi+i-1).push_back(1);
        insitu.tg.amplitudes.at(csi+i-1).push_back(k1amp);
        insitu.tg.phases.at(csi+i-1).push_back(k1phase);
      }

      if(fabs(o1amp- 777.7) > high_tolerance){
        insitu.tg.periods.at(csi+i-1).push_back(o1_period);
        insitu.tg.period_titles.at(csi+i-1).push_back("O1");
        insitu.tg.tide_booleans.at(csi+i-1).push_back(1);
        insitu.tg.amplitudes.at(csi+i-1).push_back(o1amp);
        insitu.tg.phases.at(csi+i-1).push_back(o1phase);
      }
     
      adjust_tg_phases(insitu,csi+i-1);
      generate_tg_timeseries(insitu,csi+i-1);
      cout<<"Station #"<<i+csi<<" ("<<insitu.tg.titles.at(csi+i-1)<<") has "<<insitu.tg.amplitudes.at(csi+i-1).size()<<" amplitudes/phases."<<endl;
    }
  }//End of while not finished with stations in this file.
  fclose(in_fp);
  
  //Update this passed-by-ref variable so next amp/phase file loads correctly.
  csi = (long long)insitu.tg.titles.size();
}

void adjust_tg_phases(insitu_s &insitu, long long index){
  /**********************************************************************
  Purpose:  This function converts Greenwich phase lag of tide gauge data
            to J2000 phases.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          index - current index in insitu.tg vectors.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long j;
  //Convert this station's phases from Greenwich phase lags to J2000 phases.

  doodson_s doodson;
  doodson.type=3;//Simon 1994.
  doodson.time = 0;
  for(j=0;j<(long long)insitu.tg.periods.at(index).size();j++){
    doodson.input_period=insitu.tg.periods.at(index).at(j);
    greenwich_phase_at_epoch(doodson);
    insitu.tg.phases.at(index).at(j) = restrict_degrees(-insitu.tg.phases.at(index).at(j) + doodson.phase);
  }
}

void generate_tg_timeseries(insitu_s &insitu, long long index){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          index - current index in insitu.tg vectors.
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long j,k;
  //Used to generate time series when loading amp/phase data.
  long long start_time, end_time, interval;
  double current_omega,cos_amp,sin_amp,temp_height;
  
  //Custom time conversion functions require this structure as a container.
  time_conversion_s time_conversion; time_conversion.initialized=0;
  time_conversion.calendar_time.resize(6,0);

  period_info_s period_info;

  double mean;

  cout<<"Generating time series..."<<endl;

  //Create list of times (utc and fes_jul).
  //At first just hourly times from 2000 to 2005.
  start_time = 0; end_time = 2*365*24*3600; interval = 3600;
  for(j=start_time;j<end_time;j+=interval){
    insitu.tg.times.at(index).push_back(j);
    time_conversion.utc_seconds = j;
    utc2fes_jul(time_conversion);//Convert UTC seconds to FES Jul days
    insitu.tg.fes_julian_times.at(index).push_back(time_conversion.fes_jul_days);
  }
  insitu.tg.sealevels.at(index).resize(insitu.tg.times.at(index).size());
  insitu.tg.airpressures.at(index).resize(insitu.tg.times.at(index).size());
  //Calculate contribution to sealevel for all AP periods
  //separately, so that is_this_period_recognized() only runs once
  //per model period rather than once per time per model period.
  for(j=0;j<(long long)insitu.tg.periods.at(index).size();j++){
    is_this_period_recognized(insitu.tg.periods.at(index).at(j),period_info);
    if(period_info.tide == 1){
      current_omega = period_info.omega;

      cos_amp=insitu.tg.amplitudes.at(index).at(j)*cos(insitu.tg.phases.at(index).at(j));
      sin_amp=insitu.tg.amplitudes.at(index).at(j)*sin(insitu.tg.phases.at(index).at(j));
      //Calculate this period's contribution to sealevel height
      //at all times for which tide gauge data exists.
      for(k=0;k<(long long)insitu.tg.times.at(index).size();k++){
        temp_height = cos_amp*cos(current_omega*(insitu.tg.times.at(index).at(k))) + sin_amp*sin(current_omega*(insitu.tg.times.at(index).at(k)));
        insitu.tg.sealevels.at(index).at(k) += temp_height;
      }//End of loop through times in tg.times.
    }//End of "is this a recognized period?"
  }//End of loop through tide gauge's periods.

  //Calculate and subtract the mean.
  mean = 0.0;
  for(k=0;k<(long long)insitu.tg.sealevels.at(index).size();k++) mean += insitu.tg.sealevels.at(index).at(k);
  mean /= (double)insitu.tg.sealevels.at(index).size();
  cout<<"Mean (subtracted): "<<mean<<endl;
  for(k=0;k<(long long)insitu.tg.sealevels.at(index).size();k++) insitu.tg.sealevels.at(index).at(k) -= mean;

  //DEBUG!
  //Print out first bit of the time series at this station.
  int length_of_time_series = 20;
  if(1==2){
    if(length_of_time_series > (long long)insitu.tg.times.at(index).size()) length_of_time_series = (long long)insitu.tg.times.at(index).size();
    for(k=0;k<length_of_time_series;k++){
      cout<<"Time: "<<insitu.tg.times.at(index).at(k)<<" has height: "<<insitu.tg.sealevels.at(index).at(k)<<" cm."<<endl;
    }//End of loop through times.
  }

  cout<<"Station #"<<index+1<<" ("<<insitu.tg.titles.at(index)<<") has "<<insitu.tg.sealevels.at(index).size()<<" sea level measurements."<<endl;
}

void create_synth_timeseries(insitu_s &insitu){
  /**********************************************************************
  Purpose:  This function creates a synth time series using synth_interval,
            num_synth_points, and synth_offset. It fills in times
            and fes_julian_times
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  time_conversion_s time_conversion;

  cout<<"Creating synth time series..."<<endl;
  insitu.tg.times.clear();
  //Only a single grid point at a time.
  insitu.tg.times.resize(1);
  insitu.tg.times.front().resize(insitu.num_synth_points);
  for(i=0;i<insitu.num_synth_points;i++){
    insitu.tg.times.front().at(i) = insitu.synth_offset + i*insitu.synth_interval;
  }
  //Now convert to FES Julian days.
  insitu.tg.fes_julian_times.resize(1);
  insitu.tg.fes_julian_times.front().resize(insitu.num_synth_points);
  for(i=0;i<insitu.num_synth_points;i++){
    time_conversion.utc_seconds = insitu.tg.times.front().at(i);
    //Convert this UTC time to a FES julian date:
    utc2fes_jul(time_conversion);
    insitu.tg.fes_julian_times.front().at(i) = time_conversion.fes_jul_days;
  }
  cout<<"Done."<<endl;
}

void generate_model_timeseries(insitu_s &insitu,
                               model_options_s &model_options){
  /**********************************************************************
  Purpose:  This function generates a FES signal at each of the insitu
            stations by calling fes_access().

            THIS SHOULD BE GENERALIZED TO OTHER MODELS LATER!!!
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,k;
  double mean;
  fes_data_s fes_data;//For FES predictions loaded from FES routines.

  switch(model_options.tg_type){
    default: cout<<"!!!!WARNING!!!!!! model_options.tg_type "<<model_options.tg_type<<" isn't recognized."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:{
      cout<<"WARNING, THIS CODE HAS NEVER BEEN TESTED AND IS PROBABLY OBSOLETE!"<<endl;
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 2:{//Run FES prediction routines, put into fes_data then insitu.

      cout<<"Generating autoFES at tide gauge locations using 1-by-1 code."<<endl;

      //Load the list of periods of the tidal constituents and their names.
      //So that the folder names are correct:
      insitu.tg.model.options = model_options;
      //So that it loads FES.
      insitu.tg.model.options.type = 1;
      define_model_titles_and_periods(insitu.tg.model);

      fes_data.type = 1;//Load using old FES code that loads into fes_data.
      fes_data.verbose = 0;

      //Initialize vectors.
      insitu.tg.model.sealevels.clear();
      insitu.tg.model.sealevels.resize(insitu.tg.grid.lat.size());
      for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
        insitu.tg.model.sealevels.at(i).resize(insitu.tg.fes_julian_times.at(i).size(),0.0);
      }//End of loop through tide gauge stations.

      //Calculate time series at all tide gauge stations.
      for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
        cout<<"Generating time series at tide gauge station "<<i+1<<" of "<<insitu.tg.grid.lat.size()<<endl;

        //Copy station coords, times to the fes_data structure.
        fes_data.lat	= insitu.tg.grid.lat.at(i);
        fes_data.lon	= insitu.tg.grid.lon.at(i);
        fes_data.julian_times = insitu.tg.fes_julian_times.at(i);

        cout<<"fes_access() is obtaining FES predictions at this station..."<<endl;
        fes_access(fes_data,insitu);

        //Debug
        if(1==2){
          for(k=0;k<(long long)insitu.tg.model.sealevels.at(i).size();k++){
            cout<<"FES prediction for FES Julian date "<<fes_data.julian_times.at(k)<<" is "<<fes_data.sealevels.at(k)<<endl;
          }
        }

        //Copy FES heights to the insitu structure.
        cout<<"Copying FES predictions to the insitu structure..."<<endl;
        for(k=0;k<(long long)insitu.tg.model.sealevels.at(i).size();k++){
          insitu.tg.model.sealevels.at(i).at(k) = fes_data.sealevels.at(k);
        }

        //Loop through all model sealevel heights and calculate the mean.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.model.sealevels.at(i).size();k++){
          mean += insitu.tg.model.sealevels.at(i).at(k);
        }
        mean /= (double)insitu.tg.fes_julian_times.at(i).size();

        cout<<"Mean sealevel (subtracted): "<<mean<<endl;

        //Loop through all model sealevel heights and subtract the mean.
        for(k=0;k<(long long)insitu.tg.model.sealevels.at(i).size();k++){
          insitu.tg.model.sealevels.at(i).at(k) -= mean;
        }

      }//End of loop through tide gauge stations.
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 3:{//Run FES prediction routines, put in insitu directly.

      cout<<"Generating autoFES at tide gauge locations using direct code."<<endl;
      //Load the list of periods of the tidal constituents and their names.
      //So that the folder names are correct:
      insitu.tg.model.options = model_options;
      //So that it loads FES.
      insitu.tg.model.options.type = 1;
      define_model_titles_and_periods(insitu.tg.model);
      fes_data.type = 2;//Load directly into insitu.
      fes_data.verbose = 0;

      //Initialize vectors.
      insitu.tg.model.sealevels.clear();
      insitu.tg.model.sealevels.resize(insitu.tg.grid.lat.size());
      for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
        insitu.tg.model.sealevels.at(i).resize(insitu.tg.fes_julian_times.at(i).size(),0.0);
      }//End of loop through tide gauge stations.
      fes_access(fes_data,insitu);

      break;
    }
  }//End switch-case
}

int fes_access(fes_data_s &fes_data, insitu_s &insitu){
  /**********************************************************************
  Purpose:  This function accesses the FES 2004 data and prediction
            routines. It's originally based on testfes.c which came with
            FES 2004.

            The first type merely read in a set of coordinates along with
            a series of julian dates (relative to 1950-1-1 00:00.00) at
            which to calculate the sealevel in cm.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  fesData*	shortTide = NULL;
  fesData*	radialTide = NULL;
  int		rc	= 0;
  double	tide;
  double	lp;
  double	load;
  double	loadlp;
  long long i,k;//Counters.
  double mean;

  string datafolder(gaiafolder()); datafolder.append("data/FES2004/data");

  switch(fes_data.type){
    default: cout<<"!!!!WARNING!!!!!! fes_data.type "<<fes_data.type<<" isn't recognized."<<endl;
    /////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////
    case 1:{//Load the pure tides for the Julian times and lat/lon given.
      //Initialize memory for FES algorithms
      rc = fesNew(&shortTide, FES_TIDE, FES_IO, datafolder.c_str());
      if(rc != FES_SUCCESS) goto onError;
      rc = fesNew(&radialTide, FES_RADIAL, FES_IO, datafolder.c_str());
      if(rc != FES_SUCCESS) goto onError;

      fes_data.sealevels.clear();
      fes_data.sealevels.resize(fes_data.julian_times.size(),0.0);

      if(fes_data.verbose) printf("%12s %9s %9s %9s %9s %9s %9s %9s\n","JulDay","Latitude","Longitude","Short_tid","LP_tid","Pure_Tide","Geo_Tide","Rad_Tide");

      for(i=0;i<(long long)fes_data.julian_times.size();i++){
        //Compute tide
        rc = fesCore(shortTide, fes_data.lat, fes_data.lon, fes_data.julian_times.at(i), &tide, &lp);
        if(rc == FES_SUCCESS){
          //Compute load tide
          rc = fesCore(radialTide, fes_data.lat, fes_data.lon, fes_data.julian_times.at(i), &load, &loadlp);
          if(rc != FES_NO_DATA && rc == FES_SUCCESS){
            if(fes_data.verbose) printf("%12.5f %9.3f %9.3f %9.3f %9.3f %9.3f %9.3f %9.3f\n",fes_data.julian_times.at(i),fes_data.lat,fes_data.lon,tide,lp,tide+lp,tide+lp+load,load);
            fes_data.sealevels.at(i) = tide+lp;
          }
        }
        else goto onError;
      }
      break;
    }
    /////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////
    case 2:{//Load the pure tides directly into sealevels.
      //Initialize memory for FES algorithms
      rc = fesNew(&shortTide, FES_TIDE, FES_IO, datafolder.c_str());
      if(rc != FES_SUCCESS) goto onError;
      rc = fesNew(&radialTide, FES_RADIAL, FES_IO, datafolder.c_str());
      if(rc != FES_SUCCESS) goto onError;
      if(fes_data.verbose) printf("%12s %9s %9s %9s %9s %9s %9s %9s\n","JulDay","Latitude","Longitude","Short_tid","LP_tid","Pure_Tide","Geo_Tide","Rad_Tide");
      //Calculate time series at all tide gauge stations.
      for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
        //Loop through all times and load model predictions.
        mean = 0.0;
        for(k=0;k<(long long)insitu.tg.fes_julian_times.at(i).size();k++){
          //Compute tide
          rc = fesCore(shortTide, insitu.tg.grid.lat.at(i), insitu.tg.grid.lon.at(i), insitu.tg.fes_julian_times.at(i).at(k), &tide, &lp);
          if(rc == FES_SUCCESS){
            //Compute load tide
            rc = fesCore(radialTide, insitu.tg.grid.lat.at(i), insitu.tg.grid.lon.at(i), insitu.tg.fes_julian_times.at(i).at(k), &load, &loadlp);
            if(rc != FES_NO_DATA && rc == FES_SUCCESS){
              if(fes_data.verbose) printf("%12.5f %9.3f %9.3f %9.3f %9.3f %9.3f %9.3f %9.3f\n",insitu.tg.fes_julian_times.at(i).at(k),insitu.tg.grid.lat.at(i),insitu.tg.grid.lon.at(i),tide,lp,tide+lp,tide+lp+load,load);
              insitu.tg.model.sealevels.at(i).at(k) = tide+lp;
            }
          }
          else goto onError;
          mean += insitu.tg.model.sealevels.at(i).at(k);
        }//End of loop through times.
        mean /= (double)insitu.tg.model.sealevels.at(i).size();
        if(fes_data.verbose) cout<<"Mean sealevel (subtracted): "<<mean<<endl;
        //Loop through all model sealevel heights and subtract the mean.
        for(k=0;k<(long long)insitu.tg.model.sealevels.at(i).size();k++) insitu.tg.model.sealevels.at(i).at(k) -= mean;

      }//End of loop through stations.

      break;
    }
  }//End of switch-case.

  goto onTerminate;

onError:
  printf("#ERROR : %s\n", fesErr2String(rc)),
  rc = 1;

onTerminate:
  //Free memory for FES algorithms
  fesDelete(shortTide);
  fesDelete(radialTide);

  return rc;
}

void generate_input_timeseries(insitu_s &insitu,
                               results_s &results,
                               grid_s &grid,
                               int subtract_mean){
  /**********************************************************************
  Purpose: Interps amps/phases from results to in situ stations, calcs
          residual sealevel height from TIDES ONLY at in situ stations
          at times that in situ stations have sealevel measurements.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k,l;//Counters.
  double current_omega,cos_amp,sin_amp,temp_height;//Temp variables.
  period_info_s period_info;
  double mean;//Calculates and removes the mean.
  double denominator=0.0;//Used in weighted average of nearest gr pts.
  double numerator;//Used in weighted average of nearest gr pts.

  int verbose=0;//1/0 turns on/off lots of detailed couts.

  cout<<"Generating input time series at in situ stations for input mascon signal.  Only tide values are used- const,trend,semi-annual,annual are not included.  S2 should perhaps also be split up into its components, and the nontidal part removed as well...right?"<<endl;

  //debug
  if(1==2){
    for(i=0;i<(long long)results.options.output_type.size();i++){
      cout<<"results.options.output_type["<<i<<"]: "<<results.options.output_type.at(i)<<endl;
      cout<<"results.options.output_tide["<<i<<"]: "<<results.options.output_tide.at(i)<<endl;
    }
    for(i=0;i<(long long)results.options.parameter_type.size();i++){
      cout<<"results.options.parameter_type["<<i<<"]: "<<results.options.parameter_type.at(i)<<endl;
      cout<<"results.options.parameter_tide["<<i<<"]: "<<results.options.parameter_tide.at(i)<<endl;
      cout<<"results.options.parameter["<<i<<"]: "<<results.options.parameter.at(i)<<endl;
    }
    cout<<endl;
  }

  //Uses vectors created in insitu_initialize() to average
  //residual input signal to each tide gauge station's location.
  //Amps and phases are converted to sines and cosines during
  //averaging, then converted back to amps/phases after avging.
  //It then uses those averaged amps and phases to generate a
  //time series at the station at the times that the station has
  //data.  If the station has no time series, it just saves the
  //amps and phases.
  //Convert any amp/phase output to sin/cos so they can be averaged.
  convert_results_from_ampphase_to_sincos(results);

  //Initialize vectors.
  insitu.tg.input_sealevels.resize(insitu.tg.grid.lat.size());
  for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
    insitu.tg.input_sealevels.at(i).clear();
    insitu.tg.input_sealevels.at(i).resize(insitu.tg.sealevels.at(i).size(),0.0);
  }//End of loop through tide gauge stations.

  //Average sines,cosines, const/lin from gr pts around each station,
  //using weighted factors computed in insitu_initialization() and the
  //results just calculated in perform_analysis()
  //Use to form an interpolated guess of what the input residual time
  //series would be, then calculate at all times held in the tide
  //gauge time series (if none, just save the cosamps and sinamps and
  //the more general vector...)
  insitu.tg.output_values.clear();
  insitu.tg.output_values.resize((long long)insitu.tg.grid.lat.size());
  //Loop through each station.
  for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){

    if(verbose) cout<<"Station: "<<insitu.tg.titles.at(i)<<endl;
    insitu.tg.output_values.at(i).resize((long long)results.options.output_type.size());

    //For each station, average the output values at the nearby grid
    //points using weighting factors and save them in mult_output_values
    for(j=0;j<(long long)results.options.output_type.size();j++){
      //Sum all output values at all grid points near each
      //station multiplied by their (precomputed) weighting factors.
      denominator = 0.0;
      numerator = 0.0;
      for(k=0;k<(long long)insitu.tg.nearby_grid_indices.at(i).size();k++){

        if(verbose) cout<<"Output "<<j+1<<" of "<<(long long)results.options.output_type.size()<<" (type "<<(long long)results.options.output_type.at(j)<<")  has value "<<setprecision(5)<<results.outputs.at(j).at(insitu.tg.nearby_grid_indices.at(i).at(k))<<" and weight "<<setprecision(9)<<insitu.tg.weighting_factors.at(i).at(k)<<endl;


        //2009-09-09 - Added division by wcover.
        if(grid.wcover.at(insitu.tg.nearby_grid_indices.at(i).at(k)) > 0.0){
          denominator += insitu.tg.weighting_factors.at(i).at(k);

          numerator += results.outputs.at(j).at(insitu.tg.nearby_grid_indices.at(i).at(k)) * insitu.tg.weighting_factors.at(i).at(k)/grid.wcover.at(insitu.tg.nearby_grid_indices.at(i).at(k));
        }
        else{
          cout<<"!!!WARNING!!! POINTS WITH NO wcover AREN'T SUPPOSED TO BE HERE!!!"<<endl;
          denominator += insitu.tg.weighting_factors.at(i).at(k);

          numerator += results.outputs.at(j).at(insitu.tg.nearby_grid_indices.at(i).at(k)) * insitu.tg.weighting_factors.at(i).at(k);
        }//*/

      }//End of loop through nearby grid points for this station.
      insitu.tg.output_values.at(i).at(j) = numerator/denominator;

      if(verbose) cout<<"Weighted average: "<<insitu.tg.output_values.at(i).at(j)<<endl;
    }//End of loop through output_type.
  }//End of station loop.

  //Calculate residual time series at tide gauge stations w/ time series
  cout<<"Remember to convert input.epoch from GPS time to UTC time!"<<endl;
  for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
    cout<<"Generating input time series at station "<<i+1<<" of "<<insitu.tg.grid.lat.size()<<endl;

    mean = 0.0;

    //Calculate contribution to sealevel for all input periods
    //separately, so that is_this_period_recognized() only runs once
    //per model period rather than once per time per model period.

    //Should loop through parameter_type rather than output_type
    //because that way is_this_period_recognized() runs half as many
    //times, and because I don't need to know details like amp/phase
    //because I know insitu.tg.output_values[][]
    //is automatically in cosamp,sinamp format if parameter_type==101.
    //Still, need 2 know current position in ins.t_g.m_o_v.  That's l
    l=0;
    for(j=0;j<(long long)results.options.parameter_type.size();j++){
      //Only bother including parameters that have tide==1.
      if(results.options.parameter_tide.at(j) == 1){
        is_this_period_recognized(results.options.parameter.at(j),period_info);
        //In rads/sec.
        current_omega = period_info.omega;
        //cout<<"Calculating contribution from input residual tidal parameter with period "<<period_info.title<<" ("<<j+1<<" of "<<results.options.parameter_type.size()<<")- though not all will be used because some parameters aren't tides."<<endl;
        cos_amp=insitu.tg.output_values.at(i).at(l);
        sin_amp=insitu.tg.output_values.at(i).at(l+1);

        l+=2;
        //Calculate this period's contribution to sealevel height
        //at all times for which tide gauge data exists.
        for(k=0;k<(long long)insitu.tg.times.at(i).size();k++){
          double current_time = insitu.tg.times.at(i).at(k);// - fes_utc_int_epoch;
          double current_phase = restrict_radians(current_omega*current_time);

          temp_height = cos_amp*cos(current_phase)+sin_amp*sin(current_phase);
          mean += temp_height;
          insitu.tg.input_sealevels.at(i).at(k) += temp_height;
        }//End of loop through times in tg.times.
      }//End of "is this parameter a tidal period?" statement.
      //New parameter types should be added here.
      else if(results.options.parameter_type.at(j)==0) l++;
      else if(results.options.parameter_type.at(j)==1) l++;
      else if(results.options.parameter_type.at(j)==101) l+=2;
      else cout<<"WARNING!  Non-tidal parameter ("<<j<<" of "<<results.options.parameter_type.size()<<") appears to have an unrecognized paramateter_type: "<<results.options.parameter_type.at(j)<<endl;
    }//End of loop through input parameters.
    if(subtract_mean != 0){
      //Remove mean from this time series- 1st divide by # o times
      mean /= (double)insitu.tg.times.at(i).size();
      cout<<"Mean (subtracted): "<<mean<<endl;
      //Loop through all model sealevel heights and subtract the mean.
      for(k=0;k<(long long)insitu.tg.input_sealevels.at(i).size();k++) insitu.tg.input_sealevels.at(i).at(k) -= mean;
    }
  }//End of loop through tide gauge stations.

  //If needed, convert all sin/cos output back to amp/phase
  if(results.conversion_to_sincos == 2) convert_results_from_sincos_to_ampphase(results);
}

void make_timeseries_plots(insitu_s &insitu){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;

  //Specify the time period over which to plot the time series.
  long long delay = 0;//3600*24*365*2;//In seconds, from first time in series.
  long long duration = 3600*24*365;

  //Needed by write_output().
  results_s temp_results;
  string base = "stations";
  grid_s temp_grid;
  plot_options_s temp_plot_options;
  ancillary_s temp_ancillary;

  //Output directly to the output temp folder.
  temp_plot_options.outputfolder = gaiafolder();
  temp_plot_options.outputfolder.append("output/temp/");

  //Override the usual map output.
  temp_results.options.output_choice = 2;

  //Used to push_back vectors of dbl vectors
  vector<double> temp_v_double;
  //push_back vs of vs of dbl vectors
  vector< vector<double> > temp_vv_double;
  //push_back vs of string vectors
  vector<string> temp_v_string;

  //Loop through stations.
  for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){

    temp_results.xy.titles.push_back(insitu.tg.titles.at(i));
    temp_results.xy.x_units.push_back("Time (hours)");
    temp_results.xy.y_units.push_back("Height (cm)");

    //This is a brand new plot, so push_back using temp vectors/vvs.
    temp_results.xy.x_values.push_back(temp_vv_double);
    temp_results.xy.y_values.push_back(temp_vv_double);
    temp_results.xy.legends.push_back(temp_v_string);

    //This plot has 2 lines in it.
    temp_results.xy.x_values.at(i).resize(2);
    temp_results.xy.y_values.at(i).resize(2);
    temp_results.xy.legends.at(i).push_back(insitu.name1);
    temp_results.xy.legends.at(i).push_back(insitu.name2);

    //Use delay to figure out which index to start at.
    long long first_index=-1;
    long long series_length=insitu.tg.times.at(i).size();
    long long j=0;
    while(first_index == -1 and j < series_length){
      if(insitu.tg.times.at(i).at(j) - insitu.tg.times.at(i).front() >= delay){
        first_index=j;
      }
      j++;
    }
    if(first_index==-1) cout<<"WARNING!!! first_index == -1."<<endl;

    long long first_time = insitu.tg.times.at(i).at(first_index);
    long long last_time = first_time + duration;

    //Cycle through points from the first_index, but don't go past the end.
    j=first_index;
    while(j < series_length){
      //If this point is before last_time, add it to plot.
      if(insitu.tg.times.at(i).at(j) < last_time){
        //series1.
        temp_results.xy.x_values.at(i).front().push_back((insitu.tg.times.at(i).at(j)-first_time)/3600.0);
        temp_results.xy.y_values.at(i).front().push_back(insitu.series1.at(i).at(j));

        //series2.
        temp_results.xy.x_values.at(i).at(1).push_back((insitu.tg.times.at(i).at(j)-first_time)/3600.0);
        temp_results.xy.y_values.at(i).at(1).push_back(insitu.series2.at(i).at(j));
        j++;
      }
      else j = series_length;//Stops immediately.
    }
  }

  write_output(base, temp_results, temp_grid, temp_plot_options, 1);
}

string sec2cal(long long i){
  /**********************************************************************
  Purpose: This function converts time in J2000 seconds to a
           string showing the calendar date.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  string output;
  int pos;//Used to remove \n at end of lines.

  //Rawtime is in UNIX time, rel to midnight Jan 1, 1970, hence the sum.
  time_t rawtime = i+946728000;
  output = asctime(gmtime(&rawtime));
  //Remove the \n at the end of the line.
  if((pos=output.find("\n"))!=(int)string::npos){
    output.swap(output.erase(pos,2));
  }
  return output;
}

void compare_station_timeseries(insitu_s &insitu){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,length_of_time_series=10;
  double rms_series1=0.0,rms_series2=0.0,rms_resid=0.0;
  long long k=0,l; //Big counters- more of them for amplitude fitting code.

  results_s ar_series1,ar_series2,ar_diff;//Just as with rmss!

  //Used for converting timespans in seconds to human readable units.
  timespan_conversion_s timespan_conversion;

  //Fit amps to time series using the periods in the vector
  //insitu.tg.model.periods.

  //Loop through tide gauge stations.
  for(i=0;i<(long long)insitu.tg.titles.size();i++){
    cout<<"----------------------------------------"<<endl;
    cout<<"Station: "<<insitu.tg.titles.at(i)<<endl;
    cout<<"lat: "<<insitu.tg.grid.lat.at(i)<<endl;
    cout<<"lon: "<<insitu.tg.grid.lon.at(i)<<endl;
    cout<<"Number of data points: "<<insitu.series1.at(i).size()<<endl<<endl;

    timespan_conversion.seconds=insitu.tg.times.at(i).at(1)-insitu.tg.times.at(i).front();
    cout<<"Time between data points:"<<sec2human(timespan_conversion)<<endl;

    //Convert first and last times to character times.
    cout<<"First data point is: "<<sec2cal(insitu.tg.times.at(i).front())<<endl;
    cout<<"Last data point is : "<<sec2cal(insitu.tg.times.at(i).back())<<endl;

    rms_series1 = 0.0; rms_series2 = 0.0; rms_resid = 0.0;

    //Requires GSL libraries.
    #ifdef GSL_HERE
      least_squares_s least_squares1,least_squares2,least_squares3;
      //Record the dimension of b (and first dimension of RECTANGULAR A) here.
      least_squares1.dimension = insitu.tg.model.periods.size()*2;
      least_squares2.dimension = least_squares1.dimension;
      least_squares3.dimension = least_squares1.dimension;
      long long rows = insitu.tg.times.at(i).size();//Num of data pts > dimension (i.e. num of params).

      //cout<<"Allocating GSL A matrix with dimensions "<<rows<<", "<<least_squares.dimension<<" and GSL b vector with dimension "<<rows<<endl;
      //Initialize "A" matrix- correct dimensions and populated with 0.0.
      least_squares1.A = gsl_matrix_alloc(rows, least_squares1.dimension);
      gsl_matrix_set_zero(least_squares1.A);
      least_squares2.A = gsl_matrix_alloc(rows, least_squares2.dimension);
      gsl_matrix_set_zero(least_squares2.A);
      least_squares3.A = gsl_matrix_alloc(rows, least_squares3.dimension);
      gsl_matrix_set_zero(least_squares3.A);

      //Initialize "b" vector- correct dimension and populated with 0.0.
      least_squares1.b = gsl_vector_alloc(rows);
      gsl_vector_set_zero(least_squares1.b);
      least_squares2.b = gsl_vector_alloc(rows);
      gsl_vector_set_zero(least_squares2.b);
      least_squares3.b = gsl_vector_alloc(rows);
      gsl_vector_set_zero(least_squares3.b);

      //loop through times
      for(j=0;j<(long long)insitu.tg.times.at(i).size();j++){
        rms_series1 += pow(insitu.series1.at(i).at(j),2);
        rms_series2 += pow(insitu.series2.at(i).at(j),2);
        rms_resid += pow(insitu.series1.at(i).at(j)-insitu.series2.at(i).at(j),2);
        gsl_vector_set(least_squares1.b, j, insitu.series1.at(i).at(j));
        gsl_vector_set(least_squares2.b, j, insitu.series2.at(i).at(j));
        gsl_vector_set(least_squares3.b, j, insitu.series1.at(i).at(j)-insitu.series2.at(i).at(j));
        double current_time = insitu.tg.times.at(i).at(j);// - fes_utc_int_epoch;
        //Define partials of all sin/cos parameters to be estimated
        l=0;
        for(k=0;k<(long long)insitu.tg.model.periods.size();k++){
          double current_phase = restrict_radians(insitu.tg.model.omegas.at(k)*current_time);
          gsl_matrix_set(least_squares1.A, j, l, cos(current_phase));
          l++;
          gsl_matrix_set(least_squares1.A, j, l, sin(current_phase));
          l++;
        }
      }//End of loop through times.
      
      //Copy identical A matrices. 
      gsl_matrix_memcpy(least_squares2.A,least_squares1.A);
      gsl_matrix_memcpy(least_squares3.A,least_squares1.A);
      
      //These simply held the sum of squares until now.
      rms_series1 = sqrt(rms_series1/(double)insitu.tg.times.at(i).size());
      rms_series2 = sqrt(rms_series2/(double)insitu.tg.times.at(i).size());
      rms_resid = sqrt(rms_resid/(double)insitu.tg.times.at(i).size());

      //Series1 amplitudes:
      //Solve normal equations
      //residual is "e" in b=Ax+e.
      gsl_vector * residual1 = gsl_vector_alloc(rows);
      gsl_vector * x1 = gsl_vector_alloc(least_squares1.dimension);
      gsl_vector_set_zero(residual1);
      //The vector tau contains internal QR decomp information.
      gsl_vector * tau1 = gsl_vector_alloc(least_squares1.dimension);
      gsl_vector_set_zero(tau1);
      //Solve normal equations
      gsl_linalg_QR_decomp(least_squares1.A, tau1);
      gsl_linalg_QR_lssolve(least_squares1.A, tau1, least_squares1.b, x1, residual1);
      copy_x_to_ampphase(x1,ar_series1,insitu,i);
      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares1.A);
      gsl_vector_free(least_squares1.b);
      gsl_vector_free(x1);
      gsl_vector_free(tau1);
      gsl_vector_free(residual1);

      //Series2 amplitudes:
      //Solve normal equations
      //residual is "e" in b=Ax+e.
      gsl_vector * residual2 = gsl_vector_alloc(rows);
      gsl_vector * x2 = gsl_vector_alloc(least_squares2.dimension);
      gsl_vector_set_zero(residual2);
      //The vector tau contains internal QR decomp information.
      gsl_vector * tau2 = gsl_vector_alloc(least_squares2.dimension);
      gsl_vector_set_zero(tau2);
      //Solve normal equations
      gsl_linalg_QR_decomp(least_squares2.A, tau2);
      gsl_linalg_QR_lssolve(least_squares2.A, tau2, least_squares2.b, x2, residual2);
      copy_x_to_ampphase(x2,ar_series2,insitu,i);
      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares2.A);
      gsl_vector_free(least_squares2.b);
      gsl_vector_free(x2);
      gsl_vector_free(tau2);
      gsl_vector_free(residual2);

      //Difference amplitudes:
      //Solve normal equations
      //residual is "e" in b=Ax+e.
      gsl_vector * residual3 = gsl_vector_alloc(rows);
      gsl_vector * x3 = gsl_vector_alloc(least_squares3.dimension);
      gsl_vector_set_zero(residual3);
      //The vector tau contains internal QR decomp information.
      gsl_vector * tau3 = gsl_vector_alloc(least_squares3.dimension);
      gsl_vector_set_zero(tau3);
      //Solve normal equations
      gsl_linalg_QR_decomp(least_squares3.A, tau3);
      gsl_linalg_QR_lssolve(least_squares3.A, tau3, least_squares3.b, x3, residual3);
      copy_x_to_ampphase(x3,ar_diff,insitu,i);
      //Free the space taken up by the various matrices and vectors.
      gsl_matrix_free(least_squares3.A);
      gsl_vector_free(least_squares3.b);
      gsl_vector_free(x3);
      gsl_vector_free(tau3);
      gsl_vector_free(residual3);

    #else 
      cout<<"compare_station_timeseries() requires GSL. Search definitions.cpp for GSL_HERE."<<endl;
    #endif

    cout<<insitu.name1<<setw(insitu.name2.length()+11)<<" RMS: "<<fixed<<setprecision(2)<<setw(5)<<rms_series1<<" (cm)."<<endl;
    cout<<insitu.name2<<setw(insitu.name1.length()+11)<<" RMS: "<<fixed<<setprecision(2)<<setw(5)<<rms_series2<<" (cm)."<<endl;
    cout<<"("<<insitu.name1<<" - "<<insitu.name2<<") RMS: "<<fixed<<setprecision(2)<<setw(5)<<rms_resid<<" (cm)."<<endl;
    cout<<"Quality: "<<fixed<<setprecision(2)<<setw(5)<<rms_series1/rms_resid<<endl<<endl;

    //Only print the amps and phases if this tide gauge has "enough"
    //points for these fits to be meaningful.
    if((int)insitu.tg.times.at(i).size() > 100){
      cout<<"Amps and phases (degrs) for: "<<setw(11)<<insitu.name1<<setw(11)<<insitu.name2<<setw(11)<<"diff"<<setw(15)<<"Quality"<<endl;
      l=0;
      for(k=l;k<(long long)insitu.tg.model.periods.size();k++){
        cout<<"Amplitude for "<<setw(13)<<insitu.tg.model.titles.at(k)<<": "<<fixed<<setprecision(2)<<setw(11)<<ar_series1.outputs.at(l).at(i)<<setprecision(2)<<setw(11)<<ar_series2.outputs.at(l).at(i)<<setprecision(2)<<setw(11)<<ar_diff.outputs.at(l).at(i)<<" cm. "<<setw(10)<<ar_series1.outputs.at(l).at(i)/ar_diff.outputs.at(l).at(i)<<endl;
        cout<<"Phase for     "<<setw(13)<<insitu.tg.model.titles.at(k)<<": "<<fixed<<setprecision(2)<<setw(11)<<ar_series1.outputs.at(l+1).at(i)<<setprecision(2)<<setw(11)<<ar_series2.outputs.at(l+1).at(i)<<setprecision(2)<<setw(11)<<ar_diff.outputs.at(l+1).at(i)<<" deg."<<endl;
        l+=2;
      }
      cout<<endl;
    }//End of "only run if this isn't FES" if statement.

    //Print out first bit of the time series at this station.
    if(1==1){
      if(length_of_time_series > (long long)insitu.tg.times.at(i).size()) length_of_time_series = (long long)insitu.tg.times.at(i).size();
      for(k=0;k<length_of_time_series;k++){
        cout<<"Time: "<<setw(11)<<insitu.tg.times.at(i).at(k)<<" has "<<insitu.name1<<" height: "<<setw(7)<<insitu.series1.at(i).at(k)<<" and "<<insitu.name2<<" height: "<<setw(7)<<insitu.series2.at(i).at(k)<<"."<<endl;
      }//End of loop through times.
    }//End of debug statement.
  }//End of loop through stations.

  //Post-solution comparisons, mainly for debug.

  //Print weighted amps/phases to compare to amps/phases in timeseries
  if(1==2){
    double cos_value,sin_value,amp,phase;
    //Loop through stations.
    for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
      cout<<"Station: "<<insitu.tg.titles.at(i)<<endl;

      //Loop through periods.
      k=0;//Stores position in sincos vector, not periods vector.
      for(j=0;j<(long long)insitu.tg.model.periods.size();j++){
        cos_value = insitu.tg.output_values.at(i).at(k);
        sin_value = insitu.tg.output_values.at(i).at(k+1);
        amp = sqrt(pow(sin_value,2)+pow(cos_value,2));
        phase = rad2deg*atan2(sin_value,cos_value);

        cout<<"Amplitude for "<<setw(13)<<insitu.tg.model.titles.at(j)<<": "<<fixed<<setprecision(2)<<setw(11)<<amp<<" cm.   "<<endl;
        cout<<"Phase for     "<<setw(13)<<insitu.tg.model.titles.at(j)<<": "<<fixed<<setprecision(2)<<setw(11)<<phase<<" deg."<<endl;

        k+=2;//Periods count twice.
      }
    }
  }//End of debug statement.

  //Print autoFES - masconFES phase diffs.
  if(1==1){
    //Loop through stations.
    for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
      cout<<"Station: "<<insitu.tg.titles.at(i)<<endl;

      //Loop through periods.
      k=0;//Stores position in sincos vector, not periods vector.
      for(j=0;j<(long long)insitu.tg.model.periods.size();j++){
        cout<<"phase diff for "<<setw(4)<<insitu.tg.model.titles.at(j)<<": "<<setw(7)<<restrict_degrees(ar_series1.outputs.at(k+1).at(i)-ar_series2.outputs.at(k+1).at(i))<<", time: "<<seconds_to_string((long long)(restrict_degrees(ar_series1.outputs.at(k+1).at(i)-ar_series2.outputs.at(k+1).at(i))/(rad2deg*insitu.tg.model.omegas.at(j))))<<"."<<endl;

        k+=2;//Periods count twice.
      }
    }
  }//End of debug statement.

  //Loop through different epochs and calculate the sum of the absolute
  //values of the auto-masconFES phase diffs. Whenever a new
  //minimum is found, report the time and sum.
  if(1==2){
    long long first_time=(long long)-2E9,last_time=-first_time,interval=1;

    vector <double> phase_offsets;//Stores offsets in degrees.
    phase_offsets.resize(insitu.tg.model.periods.size());

    //Init variables for notification function for time remaining.
    notification_s notification;
    //1st notif occurs after two loops, but it usually won't print
    //because not enough total_time will have passed (first_interval).
    //NEEDS TO BE 2 RATHER THAN 1; VERY BAD WHEN IT'S SET TO 1.
    notification.interval=2;
    //Desired min and max intervals in seconds b/w notifications.
    notification.first_interval=10;
    notification.min_interval=600, notification.max_interval=900;
    notification.start_time=-1; notification.current_time=-1;
    notification.previous_time=-1;//Needs to be initialized
    notification.prefix = "Epoch search";

    notification.starting_index = 0;//Not true! Can't handle negatives!
    notification.partial_saves_enabled = 0;

    //Used to size notification intervals.
    notification.previous_i=notification.starting_index;
    //Again, can't use starting_index because it's a LIE.
    notification.num_loops = (long long)((last_time-first_time)/interval);
    //notification.interval won't be allowed above this number
    //so that at LEAST a certain number of messages are seen.
    notification.max_notif_interval=(long long)(notification.num_loops/4);

    //Annoyingly, these need to be defined! Argh!
    least_squares_s dummy_ls;
    input_s dummy_input;
    results_s dummy_ar;

    notification.start_time = time(NULL);
    double phasediffsum;
    vector<double> lowestsums((long long)insitu.tg.grid.lat.size(),10000);//Needs to be HUGE!
    vector<double> bestoffsets((long long)insitu.tg.grid.lat.size(),69);//Needs to be recognizable.

    vector<double> frequencies(insitu.tg.model.omegas.size());
    for(j=0;j<(long long)insitu.tg.model.omegas.size();j++){
      frequencies.at(j) = rad2deg*insitu.tg.model.omegas.at(j);
    }

    //Loop through times outside stations so offsets calculated once.
    //Not using starting_index because it's a LIE!
    for(k=first_time;k<last_time;k+=interval){
      //Subtracting first_time and dividing by interval because I don't
      //feel like rewriting notify().
      notify(notification,(long long)((k-first_time)/interval),dummy_ls,dummy_input,dummy_ar);

      //be careful- how should this be done-- where have epochs already been applied?
      for(j=0;j<(long long)insitu.tg.model.periods.size();j++){
        phase_offsets.at(j) = k*frequencies.at(j);
      }

      //Loop through stations.
      for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
        phasediffsum=0;//Reset
        //Loop through periods.
        l=0;//Stores position in sincos vector, not periods vector.
        for(j=0;j<(long long)insitu.tg.model.periods.size();j++){
          phasediffsum += fabs(restrict_degrees(ar_series1.outputs.at(l+1).at(i)-(ar_series2.outputs.at(l+1).at(i)+phase_offsets.at(j))));
          l+=2;//Periods count twice.
        }
        //For this station, is this time offset best?
        if(phasediffsum<lowestsums.at(i)){
          //Only print if it's at least 0.05% better
          if((lowestsums.at(i)-phasediffsum) > 0.0005*lowestsums.at(i)){
            cout<<insitu.tg.titles.at(i)<<" has new phasediff min of "<<phasediffsum<<" at offset "<<seconds_to_string(k)<<" ("<<(lowestsums.at(i)-phasediffsum)*100.0/lowestsums.at(i)<<" % better, seconds: "<<k<<")"<<endl;
          }
          lowestsums.at(i)=phasediffsum;
          bestoffsets.at(i) = k;
        }
      }
    }
    notification.end_time = time(NULL);
    notification.time_total=notification.end_time-notification.start_time;
    cout<<notification.prefix<<" took "
      <<seconds_to_string(notification.time_total)<<endl;

    //For all stations, reveal best offset and phasediff value, and
    //print the phase differences at that best offset.
    for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
      if(lowestsums.at(i) != 10000){
        cout<<"Station: "<<insitu.tg.titles.at(i)<<" had phasediff min of "<<lowestsums.at(i)<<" at time offset "<<seconds_to_string((long long)bestoffsets.at(i))<<" (seconds: "<<bestoffsets.at(i)<<")"<<endl;

        //Loop through periods.
        k=0;//Stores position in sincos vector, not periods vector.
        for(j=0;j<(long long)insitu.tg.model.periods.size();j++){
          cout<<"Best auto-masconFES diff for "<<setw(5)<<insitu.tg.model.titles.at(j)<<": "<<restrict_degrees(ar_series1.outputs.at(k+1).at(i)-(ar_series2.outputs.at(k+1).at(i)+bestoffsets.at(i)*frequencies.at(j)))<<" degrees."<<endl;

          k+=2;//Periods count twice.
        }
      }
    }
  }//End of debug statement.

  //Loop through different epochs and calculate the sum of the absolute
  //values of the auto-masconFES phase diffs. Whenever a new
  //minimum is found, report the time and sum.
  //EXAMINE SEMI-DIRNAL AND DIURNAL SEPARATELY TO LOOK FOR EVIDENCE
  //OF A PHASE SHIFT IN EITHER TIDE BAND. IGNORES LONG PERIOD TIDES!
  if(1==2){
    long long interval=1;
    long long first_time=(long long)-seconds_in_year*51;
    long long last_time=(long long)-seconds_in_year*49;

    vector <double> phase_offsets;//Stores offsets in degrees.
    phase_offsets.resize(insitu.tg.model.periods.size());

    //Init variables for notification function for time remaining.
    notification_s notification;
    //1st notif occurs after two loops, but it usually won't print
    //because not enough total_time will have passed (first_interval).
    //NEEDS TO BE 2 RATHER THAN 1; VERY BAD WHEN IT'S SET TO 1.
    notification.interval=2;
    //Desired min and max intervals in seconds b/w notifications.
    notification.first_interval=10;
    notification.min_interval=600, notification.max_interval=900;
    notification.start_time=-1; notification.current_time=-1;
    notification.previous_time=-1;//Needs to be initialized
    notification.prefix = "Epoch search";

    notification.starting_index = 0;//Not true! Can't handle negatives!
    notification.partial_saves_enabled = 0;

    //Used to size notification intervals.
    notification.previous_i=notification.starting_index;
    //Again, can't use starting_index because it's a LIE.
    notification.num_loops = (long long)((last_time-first_time)/interval);
    //notification.interval won't be allowed above this number
    //so that at LEAST a certain number of messages are seen.
    notification.max_notif_interval=(long long)(notification.num_loops/4);

    //Annoyingly, these need to be defined! Argh!
    least_squares_s dummy_ls;
    input_s dummy_input;
    results_s dummy_ar;

    notification.start_time = time(NULL);
    double dphasediffsum,sphasediffsum;
    //Diurnal=d, semidiurnal=s.
    vector<double> dlowestsums((long long)insitu.tg.grid.lat.size(),10000);//Needs to be HUGE!
    vector<double> dbestoffsets((long long)insitu.tg.grid.lat.size(),69);//Needs to be recognizable.
    vector<double> slowestsums((long long)insitu.tg.grid.lat.size(),10000);//Needs to be HUGE!
    vector<double> sbestoffsets((long long)insitu.tg.grid.lat.size(),69);//Needs to be recognizable.
    vector<double> frequencies(insitu.tg.model.omegas.size());
    for(j=0;j<(long long)insitu.tg.model.omegas.size();j++){
      frequencies.at(j) = rad2deg*insitu.tg.model.omegas.at(j);
    }

    //Loop through times outside stations so offsets calculated once.
    //Not using starting_index because it's a LIE!
    for(k=first_time;k<last_time;k+=interval){
      //Subtracting first_time and dividing by interval because I don't
      //feel like rewriting notify().
      notify(notification,(long long)((k-first_time)/interval),dummy_ls,dummy_input,dummy_ar);

      //be careful- how should this be done-- where have epochs already been applied?
      for(j=0;j<(long long)insitu.tg.model.periods.size();j++){
        phase_offsets.at(j) = k*frequencies.at(j);
      }

      //Loop through stations.
      for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
        dphasediffsum=0; sphasediffsum=0;//Reset
        //Loop through periods.
        l=0;//Stores position in sincos vector, not periods vector.
        for(j=0;j<(long long)insitu.tg.model.periods.size();j++){
          //Define semi-diurnal as a period that's less than 18 hours.
          if(insitu.tg.model.periods.at(j) < 18*3600)
            sphasediffsum += fabs(restrict_degrees(ar_series1.outputs.at(l+1).at(i)-(ar_series2.outputs.at(l+1).at(i)+phase_offsets.at(j))));
          //Diurnal tides are > 18 hours, < 28 hours.
          //Long period tides aren't yet compared.
          else if(insitu.tg.model.periods.at(j) > 18*3600 and insitu.tg.model.periods.at(j) < 28*3600)
            dphasediffsum += fabs(restrict_degrees(ar_series1.outputs.at(l+1).at(i)-(ar_series2.outputs.at(l+1).at(i)+phase_offsets.at(j))));
          l+=2;//Periods count twice.
        }
        //For diurnal tides at this station, is this time offset best?
        if(dphasediffsum<dlowestsums.at(i)){
          //Only print if it's at least 0.05% better
          if((dlowestsums.at(i)-dphasediffsum) > 0.0005*dlowestsums.at(i)){
            cout<<insitu.tg.titles.at(i)<<" has new dphasediff min of "<<dphasediffsum<<" at offset "<<seconds_to_string(k)<<" ("<<(dlowestsums.at(i)-dphasediffsum)*100.0/dlowestsums.at(i)<<" % better, seconds: "<<k<<")"<<endl;
          }
          dlowestsums.at(i)=dphasediffsum;
          dbestoffsets.at(i) = k;
        }
        //For semidiurnal tides, is this time offset best?
        if(sphasediffsum<slowestsums.at(i)){
          //Only print if it's at least 0.05% better
          if((slowestsums.at(i)-sphasediffsum) > 0.0005*slowestsums.at(i)){
            cout<<insitu.tg.titles.at(i)<<" has new sphasediff min of "<<sphasediffsum<<" at offset "<<seconds_to_string(k)<<" ("<<(slowestsums.at(i)-sphasediffsum)*100.0/slowestsums.at(i)<<" % better, seconds: "<<k<<")"<<endl;
          }
          slowestsums.at(i)=sphasediffsum;
          sbestoffsets.at(i) = k;
        }
      }
    }
    notification.end_time = time(NULL);
    notification.time_total=notification.end_time-notification.start_time;
    cout<<notification.prefix<<" took "
      <<seconds_to_string(notification.time_total)<<endl;

    //For all stations, reveal best offset and phasediff value, and
    //print the phase differences at that best offset.
    for(i=0;i<(long long)insitu.tg.grid.lat.size();i++){
      if(dlowestsums.at(i) != 10000){
        cout<<"Station: "<<insitu.tg.titles.at(i)<<" had dphasediff min of "<<dlowestsums.at(i)<<" at time offset "<<seconds_to_string((long long)dbestoffsets.at(i))<<" (seconds: "<<dbestoffsets.at(i)<<")"<<endl;

        //Loop through periods.
        k=0;//Stores position in sincos vector, not periods vector.
        for(j=0;j<(long long)insitu.tg.model.periods.size();j++){
          cout<<"Best auto-masconFES d-diff for "<<setw(5)<<insitu.tg.model.titles.at(j)<<": "<<restrict_degrees(ar_series1.outputs.at(k+1).at(i)-(ar_series2.outputs.at(k+1).at(i)+dbestoffsets.at(i)*frequencies.at(j)))<<" degrees."<<endl;

          k+=2;//Periods count twice.
        }

        cout<<"Station: "<<insitu.tg.titles.at(i)<<" had sphasediff min of "<<slowestsums.at(i)<<" at time offset "<<seconds_to_string((long long)sbestoffsets.at(i))<<" (seconds: "<<sbestoffsets.at(i)<<")"<<endl;

        //Loop through periods.
        k=0;//Stores position in sincos vector, not periods vector.
        for(j=0;j<(long long)insitu.tg.model.periods.size();j++){
          cout<<"Best auto-masconFES s-diff for "<<setw(5)<<insitu.tg.model.titles.at(j)<<": "<<restrict_degrees(ar_series1.outputs.at(k+1).at(i)-(ar_series2.outputs.at(k+1).at(i)+sbestoffsets.at(i)*frequencies.at(j)))<<" degrees."<<endl;

          k+=2;//Periods count twice.
        }
      }
    }
  }//End of debug statement.
}

results_s analyze_timeseries(insitu_s &insitu){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  results_s ar_series1;
  //Requires GSL libraries.
  #ifdef GSL_HERE
    long long i=0,j;//i should always be zero
    long long k=0,l; //Big counters- more of them for amplitude fitting code.
    least_squares_s least_squares;
    //Record the dimension of b (and first dimension of RECTANGULAR A) here.
    least_squares.dimension = insitu.tg.model.periods.size()*2;
    long long rows = insitu.tg.times.at(i).size();//Num of data pts > dimension (i.e. num of params).

    //cout<<"Allocating GSL A matrix with dimensions "<<rows<<", "<<least_squares.dimension<<" and GSL b vector with dimension "<<rows<<endl;
    //Initialize "A" matrix- correct dimensions and populated with 0.0.
    least_squares.A = gsl_matrix_alloc(rows, least_squares.dimension);
    gsl_matrix_set_zero(least_squares.A);

    //Initialize "b" vector- correct dimension and populated with 0.0.
    least_squares.b = gsl_vector_alloc(rows);
    gsl_vector_set_zero(least_squares.b);

    //loop through times
    for(j=0;j<(long long)insitu.tg.times.at(i).size();j++){
      gsl_vector_set(least_squares.b, j, insitu.series1.at(i).at(j));
      double current_time = insitu.tg.times.at(i).at(j);// - fes_utc_int_epoch;
      //Define partials of all sin/cos parameters to be estimated
      l=0;
      for(k=0;k<(long long)insitu.tg.model.periods.size();k++){
        double current_phase = restrict_radians(insitu.tg.model.omegas.at(k)*current_time);
        gsl_matrix_set(least_squares.A, j, l, cos(current_phase));
        l++;
        gsl_matrix_set(least_squares.A, j, l, sin(current_phase));
        l++;
      }
    }//End of loop through times.
    //residual is "e" in b=Ax+e.
    gsl_vector * residual = gsl_vector_alloc(rows);
    gsl_vector * x = gsl_vector_alloc(least_squares.dimension);
    gsl_vector_set_zero(residual);
    //The vector tau contains internal QR decomp information.
    gsl_vector * tau = gsl_vector_alloc(least_squares.dimension);
    gsl_vector_set_zero(tau);
    //Solve normal equations
    gsl_linalg_QR_decomp(least_squares.A, tau);
    gsl_linalg_QR_lssolve(least_squares.A, tau, least_squares.b, x, residual);
    copy_x_to_ampphase(x,ar_series1,insitu,i);
    //Free the space taken up by the various matrices and vectors.
    gsl_matrix_free(least_squares.A);
    gsl_vector_free(least_squares.b);
    gsl_vector_free(x);
    gsl_vector_free(tau);
    gsl_vector_free(residual);
  #else 
    cout<<"analyze_timeseries() requires GSL. Search definitions.cpp for GSL_HERE."<<endl;
  #endif
  return ar_series1;
}

void define_model_titles_and_periods(model_s &model){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  switch(model.options.type){
    default: cout<<"!!!!WARNING!!!!!! model.options.type "<<model.options.type<<" isn't recognized in define_model_titles_and_periods()."<<endl;
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 1:
    case 2:
    case 3:{//FES2004

      model.title = "FES 2004";
      model.options.basefolder = "data/FES2004/data/";
/*
      model.options.filenames.push_back("M2_fes2004.asc");
      model.titles.push_back("FES04 M2");
      model.periods.push_back(m2_period);//seconds.

      model.options.filenames.push_back("K1_fes2004.asc");
      model.titles.push_back("FES04 K1");
      model.periods.push_back(k1_period);//seconds.

      model.options.filenames.push_back("S2_fes2004.asc");
      model.titles.push_back("FES04 S2");
      model.periods.push_back(s2_period);//seconds.

      model.options.filenames.push_back("O1_fes2004.asc");
      model.titles.push_back("FES04 O1");
      model.periods.push_back(o1_period);//seconds.

      model.options.filenames.push_back("S1_fes2004.asc");
      model.titles.push_back("FES04 S1");
      model.periods.push_back(s1_period);//seconds.

      model.options.filenames.push_back("P1_fes2004.asc");
      model.titles.push_back("FES04 P1");
      model.periods.push_back(p1_period);//seconds.
//*/
      model.options.filenames.push_back("Q1_fes2004.asc");
      model.titles.push_back("FES04 Q1");
      model.periods.push_back(q1_period);//seconds.

      model.options.filenames.push_back("N2_fes2004.asc");
      model.titles.push_back("FES04 N2");
      model.periods.push_back(n2_period);//seconds.

      model.options.filenames.push_back("Mf_fes2004.asc");
      model.titles.push_back("FES04 Mf");
      model.periods.push_back(mf_period);//seconds.

      model.options.filenames.push_back("Mm_fes2004.asc");
      model.titles.push_back("FES04 Mm");
      model.periods.push_back(mm_period);//seconds.
/*
      model.options.filenames.push_back("K2_fes2004.asc");
      model.titles.push_back("FES04 K2");
      model.periods.push_back(k2_period);//seconds.

      model.options.filenames.push_back("M4_fes2004.asc");
      model.titles.push_back("FES04 M4");
      model.periods.push_back(m4_period);//seconds.

      model.options.filenames.push_back("2N2_fes2004.asc");
      model.titles.push_back("FES04 2N2");
      model.periods.push_back(two_n2_period);//seconds.

      model.options.filenames.push_back("MSqm_fes2004.asc");
      model.titles.push_back("FES04 Msqm");
      model.periods.push_back(msqm_period);//seconds.

      model.options.filenames.push_back("Mtm_fes2004.asc");
      model.titles.push_back("FES04 Mtm");
      model.periods.push_back(mtm_period);//seconds.
//*/
      //Now for "dr" files which contain loading effects.
 /*  
      model.options.filenames.push_back("M2_drfes2004.asc");
      model.titles.push_back("FES04 M2 dr");
      model.periods.push_back(m2_period);//seconds.

      model.options.filenames.push_back("K1_drfes2004.asc");
      model.titles.push_back("FES04 K1 dr");
      model.periods.push_back(k1_period);//seconds.

      model.options.filenames.push_back("S2_drfes2004.asc");
      model.titles.push_back("FES04 S2 dr");
      model.periods.push_back(s2_period);//seconds.

      model.options.filenames.push_back("O1_drfes2004.asc");
      model.titles.push_back("FES04 O1 dr");
      model.periods.push_back(o1_period);//seconds.

      model.options.filenames.push_back("P1_drfes2004.asc");
      model.titles.push_back("FES04 P1 dr");
      model.periods.push_back(p1_period);//seconds.

      model.options.filenames.push_back("N2_drfes2004.asc");
      model.titles.push_back("FES04 N2 dr");
      model.periods.push_back(n2_period);//seconds.

      model.options.filenames.push_back("K2_drfes2004.asc");
      model.titles.push_back("FES04 K2 dr");
      model.periods.push_back(k2_period);//seconds.

      model.options.filenames.push_back("Q1_drfes2004.asc");
      model.titles.push_back("FES04 Q1 dr");
      model.periods.push_back(q1_period);//seconds.

      model.options.filenames.push_back("2N2_drfes2004.asc");
      model.titles.push_back("FES04 2N2 dr");
      model.periods.push_back(two_n2_period);//seconds.

      //*/
      break;
    }
    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    case 10:{//AOTIM-5
      model.title = "AOTIM-5";
      model.options.basefolder = "data/Padman/";
      model.options.filenames.push_back("padman_aotim.txt");

      model.titles.push_back("AOTIM-5 M2");
      model.periods.push_back(m2_period);//seconds.

      model.titles.push_back("AOTIM-5 S2");
      model.periods.push_back(s2_period);//seconds.

      model.titles.push_back("AOTIM-5 K1");
      model.periods.push_back(k1_period);//seconds.

      model.titles.push_back("AOTIM-5 O1");
      model.periods.push_back(o1_period);//seconds.

      model.titles.push_back("AOTIM-5 N2");
      model.periods.push_back(n2_period);//seconds.

      model.titles.push_back("AOTIM-5 K2");
      model.periods.push_back(k2_period);//seconds.

      model.titles.push_back("AOTIM-5 P1");
      model.periods.push_back(p1_period);//seconds.

      model.titles.push_back("AOTIM-5 Q1");
      model.periods.push_back(q1_period);//seconds.
      break;
    }
  }//End switch-case

  //Now calculate omegas- angular frequencies in radians per second.
  long long l;
  model.omegas.resize(model.periods.size());
  for(l=0;l<(long long)model.periods.size();l++){
    if(model.periods.at(l)==0.0) cout<<"WARNING!!! Model period #"<<l+1<<" was zero!"<<endl;
    model.omegas.at(l) = twoPi/(model.periods.at(l));
  }
}

void read_rawfes(latlon_s &rawfes){
  /**********************************************************************
  Purpose: This function loads FES2004 directly into the rawfes structure.
          Redundant values (lon=360 in addition to lon=0) are stripped out.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  FILE *in_fp;
  int N=30;//Again from FES loading code.  Num of amps/phases on 1 line.
  double buffer[FES_BUFFER];//Temporarily stores amps/phases from FES.
  long long i,j,k,im,jm,numlines=0;
  double temp;
  char s[max_length];
  string badbuffer;
  char* f_return;//Used to stop fgets return value warning.
  double min_lat,max_lat,min_lon,max_lon,deltalat,deltalon;
  long long longitude_length;

  in_fp = fopen(rawfes.filename.c_str(),"r");

  //Warn if file doesn't open correctly.
  if(in_fp == NULL) cout << "The model output file, " << rawfes.filename << ", failed to open."<<endl;
  else cout << "Loading model output data from file " << rawfes.filename << "..."<<endl;

  //Load the boundaries of the model data.
  f_return=fgets(s,max_length,in_fp); numlines++;
  sscanf(s,"%lf %lf", &min_lon, &max_lon);//FES doesn't use maxlon
  f_return=fgets(s,max_length,in_fp); numlines++;
  sscanf(s,"%lf %lf", &min_lat, &max_lat);//FES doesn't use maxlat
  f_return=fgets(s,max_length,in_fp); numlines++;
  sscanf(s,"%lf %lf", &deltalon, &deltalat);
  f_return=fgets(s,max_length,in_fp); numlines++;
  sscanf(s,"%lld %lld", &im, &jm);

  min_lon += 1E-6;//Just testing.
  min_lat += 1E-6;//Just testing.
  //cout<<"im: "<<im<<" jm: "<<jm<<endl;

  if(rawfes.discard_redundant_values != 0){
    im--;//As in grid.c, the grids should not overlap.
    longitude_length = im;//Cycle through all lons, no redundant values.
  }
  else{
    longitude_length = im-1;//If red values still there, don't write them.
  }

  f_return=fgets(s,max_length,in_fp); numlines++;
  sscanf(s,"%lf %lf", &rawfes.mask, &temp);//FES doesn't use second value(?)

  //Now that I know im and jm, clear and resize fes vectors.
  rawfes.amplitudes.clear();
  rawfes.phases.clear();

  //Now define latitude and longitude vectors.
  rawfes.lat.clear();
  rawfes.lat.resize(jm);
  rawfes.lon.clear();
  rawfes.lon.resize(im);
  for(k=0;k<(long long)rawfes.lat.size();k++){
    rawfes.lat.at(k) = min_lat+k*deltalat;
  }
  for(k=0;k<(long long)rawfes.lon.size();k++){
    rawfes.lon.at(k) = min_lon+k*deltalon;
  }

  //1st dimension is latitude.
  rawfes.amplitudes.resize(rawfes.lat.size());
  rawfes.phases.resize(rawfes.lat.size());

  //2nd dimension is longitude.
  for(k=0;k<(long long)rawfes.lat.size();k++){
    rawfes.amplitudes.at(k).resize(rawfes.lon.size(),0.0);
    rawfes.phases.at(k).resize(rawfes.lon.size(),0.0);
  }

  //Loop through latitudes
  j=0;
  while(j<(long long)rawfes.lat.size()){
    i=0;
    //cout<<"j= "<<j<<" of "<<rawfes.lat.size()<<endl;
    //Loop through longitudes using while loop because i is constantly incremented.
    while(i<longitude_length){
      //cout<<"i= "<<i<<" of "<<rawfes.lon.size()<<endl;

      //Read 30 amplitude values into buffer.
      f_return=fgets(s,max_length,in_fp); numlines++; badbuffer = s;
      k = readdoubles(s, N, buffer);
      if(k != N) cout<<"WARNING! "<<k<<"   amps in buffer instead of "<<N<<" on line "<<numlines<<":"<<endl<<badbuffer;
      //Save buffer to amplitude vector of vectors.
      for(k=0;k<N;k++)rawfes.amplitudes.at(j).at(i+k)=buffer[k];

      //Read 30 phase values into buffer.
      f_return=fgets(s,max_length,in_fp); numlines++; badbuffer = s;
      k = readdoubles(s, N, buffer);
      if(k != N) cout<<"WARNING! "<<k<<" phases in buffer instead of "<<N<<" on line "<<numlines<<":"<<endl<<badbuffer;
      //Save buffer to phase vector of vectors.
      for(k=0;k<N;k++) rawfes.phases.at(j).at(i+k) = buffer[k];

      //This loop has read "N" points in the longitude direction.
      i += N;

    }//End of loop through longitudes

    //Read redundant amplitude value.
    f_return=fgets(s,max_length,in_fp); numlines++;

    //Read redundant phase value.
    f_return=fgets(s,max_length,in_fp); numlines++;
  
    //The redundant values at the back are the same as the first lon at this lat.
    if(rawfes.discard_redundant_values == 0){
      rawfes.amplitudes.at(j).back()=rawfes.amplitudes.at(j).front();
      rawfes.phases.at(j).back()=rawfes.phases.at(j).front();
    }

    //This loop has read in a single line of latitude.
    j++;
  }//End of loop through latitudes
  fclose(in_fp);
}

void write_rawfes(latlon_s &rawfes){
  /**********************************************************************
  Purpose: This function writes FES2004 from the rawfes structure to filename.
          Redundant values (lon=360 in addition to lon=0) are added in.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  FILE *out_fp;
  int N=30;//Again from FES loading code.  Num of amps/phases on 1 line.
  long long i,j,k;
  out_fp = fopen(rawfes.filename.c_str(),"w");

  //Warn if file doesn't open correctly.
  if(out_fp == NULL) cout << "The proposed model output file, " << rawfes.filename
                    << ", failed to open."<<endl;
  else cout << "Writing model output data to file "
            << rawfes.filename << "..."<<endl;
  
  double deltalon = rawfes.lon.at(1)-rawfes.lon.at(0);
  double deltalat = rawfes.lat.at(1)-rawfes.lat.at(0);
  double minlon = *min_element(rawfes.lon.begin(),rawfes.lon.end());
  double maxlon = *max_element(rawfes.lon.begin(),rawfes.lon.end());
  
  //Correct the size of lat/lon arrays if the redundant values at lon=360 are missing.
  long long redundant_missing, longitude_length;
  if(rawfes.discard_redundant_values != 0){
    redundant_missing = 1;
    maxlon += deltalon;//This ought to make it 360.0.
    longitude_length = (long long)rawfes.lon.size();//Cycle through all lons, no redundant values.
  }
  else{
    redundant_missing = 0;
    longitude_length = (long long)rawfes.lon.size()-1;//If red values still there, don't write them.
  }
  
  //Write the boundaries of the model data.
  fprintf(out_fp,"%10.3f %9.3f\n", minlon, maxlon);
  fprintf(out_fp,"%10.3f %9.3f\n", *min_element(rawfes.lat.begin(),rawfes.lat.end()), *max_element(rawfes.lat.begin(),rawfes.lat.end()));
  fprintf(out_fp,"%.6f %12.6f    \n", deltalon, deltalat);
  fprintf(out_fp,"%8lld %7lld\n", (long long)rawfes.lon.size()+redundant_missing, (long long)rawfes.lat.size());//Add redundant values back in!
  fprintf(out_fp,"%9.3f %8.3f (mask value)                                                                      \n", rawfes.mask, rawfes.mask);//They're the same!

  //Loop through latitudes
  j=0;
  while(j<(long long)rawfes.lat.size()){
    i=0;
    //cout<<"j= "<<j<<" of "<<rawfes.lat.size()<<endl;
    //Loop through longitudes using while loop because i is constantly incremented.
    while(i<longitude_length){//Don't write redundant values twice.
      //cout<<"i= "<<i<<" of "<<rawfes.lon.size()<<endl;

      //Write 30 amplitude values.
      for(k=0;k<N;k++) fprintf(out_fp,"%7.2f", rawfes.amplitudes.at(j).at(i+k));
      fprintf(out_fp,"\n");//End of line.

      //Write 30 phase values.
      for(k=0;k<N;k++) fprintf(out_fp,"%7.2f", rawfes.phases.at(j).at(i+k));
      fprintf(out_fp,"\n");//End of line.

      //This loop has written "N" points in the longitude direction.
      i += N;

    }//End of loop through longitudes

    //Write redundant amplitude value.
    fprintf(out_fp,"%7.2f\n", rawfes.amplitudes.at(j).front());

    //Write redundant phase value.
    fprintf(out_fp,"%7.2f\n", rawfes.phases.at(j).front());

    //This loop has written a single line of latitude.
    j++;
  }//End of loop through latitudes
  fclose(out_fp);
}

void sequential_regional_analysis(
                     input_s &input,
                     grid_s &grid,
                     ancillary_s &ancillary,
                     model_s &model,
                     results_s &results,
                     results_s &regional_results,
                     results_s &overlap_results,
                     plot_options_s &plot_options,
                     plot_options_s &temp_options,
                     plot_options_s &overlap_plot_options,
                     grid_s &region,
                     grid_s &empty_region,
                     insitu_s &insitu){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k,l;
  char s[max_length];
  string base;//This string forms the base part of the output filename.
  grid_s regional_grid;

  //Loop through regions, analyzing data in each region separately,
  //then copying the regional output to directly to global results and
  //merely backing up output to disk after each region is finished.
  for(j=0;j<(long long)region.indices_to_analyze.size();j++){
    //Figure out which region index should be analyzed in this loop.
    i=region.indices_to_analyze.at(j);

    //Save current region index in results.options so that
    //perform_analysis() has access to it.
    results.options.region_index = i;
    //Save current requested rg index in results.options so that
    //the ps.* vectors have access to it.
    results.options.requested_index = j;
    //Now create prefix that will prepend all messages in this reg.
    results.options.prefix = "Region ";
    sprintf(s,"%4lld",results.options.region_index+1);
    results.options.prefix.append(s);

    //Using region.all_grid_indices and grid, create copy of the grid
    //structure with grid pts only in region "i", store in region.gr.
    define_regional_grid(grid,region,regional_grid,i);

    //Using "binindices", quickly load input data in this region ONLY
    //from disk and store it in region.input.
    //Only load input data if the analysis type requires it, and
    //only if there is at least one grid point in this region.
    //This comparison to 5,6 is clumsy- replace with something else!
    if(results.options.type != 6 and results.options.type != 5 and region.all_grid_indices.at(i).size() > 0){
      //Once I've decided that this *MIGHT* be a good time to load
      //the input in this region, I've got to see if this type uses
      //least squares, and I've found a cache subfolder, and that
      //subfolder contains a file that matches this region.  If all
      //that's true, skip loading but set region.input.time.size() to
      //a big number so that automatic checks inside perform_analysis
      //don't automatically quit when they see fewer input pts than
      //parameters to solve for.  Otherwise load as normal.
      //!!Except if the matrices are only partial!
      if(results.options.uses_least_squares != 0 and results.options.least_squares_cache_load > 0 and (results.options.ps.initial_completeness.at(results.options.requested_index) == 1 or results.options.ps.initial_completeness.at(results.options.requested_index) == 3)){
        cout<<results.options.prefix<<": Not loading input, using completed least squares cache."<<endl;
        region.input.epoch = input.epoch;//Necessary!
        region.input.time.resize(100000);//Else it's < grid.np, quits!
        //Arbitrary data needs to be copied anyway.
        region.input.arbitrary = input.arbitrary;
      }
      else region.input = load_regional_input(i,input,region);
    }
    else cout<<results.options.prefix<<": Skipping loading of input data."<<endl;

    //Write this region's input data to disk.
    if(results.options.save_regional_input_data==1){
      region.input.options.output_folder = input.options.basefolder;
      region.input.options.output_filename= "input_and_orbit_NP_cap_2000km.txt";
      cout<<"Writing region's data to file "<<region.input.options.output_folder<<region.input.options.output_filename<<endl;
      write_input(region.input);
      cout<<"Finished writing regional input to disk."<<endl;
    }

    //Only perform analysis if this region contains at least one grid
    //point.  This speeds up the program when a region mask leaves
    //some regions with no grid points.
    if(region.all_grid_indices.at(i).size() > 0){
      cout<<results.options.prefix<<": Analyzing on "<<regional_grid.lat.size()<<" grid pts.  Regions remaining: ";
      if(j==(long long)region.indices_to_analyze.size()-1) cout<<"none."<<endl;
      else{//Print out region NUMBERS, not indices.
        for(k=j+1;k<(long long)region.indices_to_analyze.size();k++) cout<<region.indices_to_analyze.at(k)+1<<" ";
        cout<<"."<<endl;
      }
      regional_results.options = results.options;
      create_output_vectors(regional_results,region.input);
 
      //Now resize the second dimension (have to do this multiple times)
      //so that the second dimension holds all grid points.
      //Note that the first dimension was set in create_output_vectors().
      for(l=0;l<(long long)regional_results.options.output_type.size();l++){
        regional_results.outputs.at(l).resize(regional_grid.lat.size(),0.0);
      }

      perform_analysis(region.input, regional_grid, ancillary, model, regional_results, plot_options, empty_region, insitu);
    }
    else cout<<results.options.prefix<<": Skipping analysis because region has no grid points.";

    //If desired, print out 0th grid point results immediately.
    if(results.options.print_immediate_results==1) for(k=0;k<(long long)regional_results.outputs.size();k++) cout<<"Grid pt at lat "<<fixed<<setprecision(1)<<regional_grid.lat.front()<<" and lon "<<fixed<<setprecision(1)<<regional_grid.lon.front()<<" has "<<regional_results.titles.at(k)<<" = "<<fixed<<setprecision(4)<<regional_results.outputs.at(k).front()<<" "<<regional_results.units.at(k)<<endl;

    //Copy "center" results from regional_results to results
    //If this is the first loop (i==0), initialize results
    //by setting sizes of vectors and copying the titles/units from
    //regional_results.
    copy_regional_results(regional_results, results, region, grid, regional_grid,i);

    //Set the output filename base to a temporary filename.
    base = "plot_data_temp";

    //Write partial results to a temporary file so that the results
    //can be sanity-checked as soon as possible.
    //Also record the names of these temp files in temp_options so
    //they can be easily rm'd after the final results are written to
    //disk.
    temp_options = plot_options;
    write_output(base, results, grid, temp_options, 0);//0 because this isn't the final write_output.

    //If desired, save output from all points in each region,
    //center and overlap. This produces a new folder with a new
    //output_filenames.txt, which has ALL parameters for ALL regions
    //(which have to be plotted separately).
    if(results.options.save_overlap_points==1){
      //overlap_results.options.copy_overlap_points==1, so
      //this statement copies overlap points too. It also initializes
      //the overlap_results structure in each loop so that previous
      //region overlap points don't show up in subsequent reg plots.
      copy_regional_results(regional_results, overlap_results, region, grid, regional_grid, i);

      //Append the title so the region is obvious for each plot.
      //Because of copy_overlap_points == 1, titles are reset each
      //loop as well, so titles don't look like Constant, Rg 1, Rg 2.
      for(k=0;k<(long long)overlap_results.titles.size();k++){
        sprintf(s,", Rg %2lld",i+1);
        overlap_results.titles.at(k).append(s);
      }
      base = "region";
      sprintf(s,"_%05lld_overlap_data",i+1);
      base.append(s);
      write_output(base, overlap_results, grid, overlap_plot_options, 0);
    }//End of "save_overlap_points".

  }//End of loop through regions.
}

void parallel_regional_analysis(
                     input_s &input,
                     grid_s &grid,
                     ancillary_s &ancillary,
                     model_s &model,
                     results_s &results,
                     results_s &regional_results,
                     results_s &overlap_results,
                     plot_options_s &plot_options,
                     plot_options_s &temp_options,
                     plot_options_s &overlap_plot_options,
                     grid_s &region,
                     grid_s &empty_region,
                     insitu_s &insitu){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k,l;
  char s[max_length];
  string base;//This string forms the base part of the output filename.

  //The following are used for fork() to enable parallel processing.
  pid_t pid;
  int rv;
  grid_s regional_grid;

  //Loop through regions, analyzing regional data in a different
  //forked process, then writing the regional results to separate
  //files which will all be combined at the end.

  //Loop through regions, start a new process for each region.
  for(j=0;j<(long long)region.indices_to_analyze.size();j++){
    //Figure out which region index should be analyzed in this loop.
    i=region.indices_to_analyze.at(j);

    switch(pid = fork()){
      default:{//This code runs in the parent process.
        //If the number of region processes already started is
        //greater than or equal to the numprocesses, wait until one
        //of the processes finishes before starting another.
        if(j+1 >= results.options.numprocesses) wait(&rv);
        break;
      }//End of parent process commands.
      case -1:{
        perror("fork");//Something went wrong!
        cout<<"Couldn't open fork!"<<endl;
        exit(1);//Parent exits.
        break;
      }
      case 0:{//This code is run in the child process.

        //This code is very similar to sequential region code.
        //So comments have been stripped- look at sequential_regional_analysis()
        //to see what these commands are intended to do.
        results.options.region_index = i;
        results.options.requested_index = j;
        results.options.prefix = "Region ";
        sprintf(s,"%4lld",results.options.region_index+1);
        results.options.prefix.append(s);

        define_regional_grid(grid,region,regional_grid,i);
        //This comparison to 5,6 is clumsy- replace w something else!
        if(results.options.type != 6 and results.options.type != 5 and region.all_grid_indices.at(i).size() > 0){
          if(results.options.uses_least_squares != 0 and results.options.least_squares_cache_load > 0 and (results.options.ps.initial_completeness.at(results.options.requested_index) == 1 or results.options.ps.initial_completeness.at(results.options.requested_index) == 3)){
            cout<<results.options.prefix<<": Skipping loading of input data because of least squares cache match."<<endl;
            region.input.time.resize(100000);//Else it's < grid.np, quits!
            region.input.epoch = input.epoch;//Necessary!
            //Arbitrary data needs to be copied anyway.
            region.input.arbitrary = input.arbitrary;
          }
          else region.input = load_regional_input(i,input,region);
        }
        else cout<<results.options.prefix<<": Skipping loading of input data."<<endl;

        //Write this region's input data to disk.
        if(results.options.save_regional_input_data==1){
          region.input.options.output_folder=input.options.basefolder;
          region.input.options.output_filename= "input_and_orbit_NP_cap_2000km.txt";
          cout<<"Writing region's data to file "<<region.input.options.output_folder<<region.input.options.output_filename<<endl;
          write_input(region.input);
          cout<<"Finished writing regional input to disk."<<endl;
        }

        if(region.all_grid_indices.at(i).size() > 0){
          cout<<results.options.prefix<<": Analyzing on "<<regional_grid.lat.size()<<" grid pts.  Regions remaining: ";
          if(j==(long long)region.indices_to_analyze.size()-1) cout<<"none."<<endl;
          else{//Print out region NUMBERS, not indices.
            for(k=j+1;k<(long long)region.indices_to_analyze.size();k++) cout<<region.indices_to_analyze.at(k)+1<<" ";
            cout<<"."<<endl;
          }
          regional_results.options = results.options;
          create_output_vectors(regional_results,region.input);
          for(l=0;l<(long long)regional_results.options.output_type.size();l++){
            regional_results.outputs.at(l).resize(regional_grid.lat.size(),0.0);
          }
          perform_analysis(region.input, regional_grid, ancillary, model, regional_results, plot_options, empty_region, insitu);
        }
        else cout<<results.options.prefix<<": Skipping analysis because region has no grid points.";

        if(results.options.print_immediate_results==1) for(k=0;k<(long long)regional_results.outputs.size();k++) cout<<"Grid pt at lat "<<fixed<<setprecision(1)<<regional_grid.lat.front()<<" and lon "<<fixed<<setprecision(1)<<regional_grid.lon.front()<<" has "<<regional_results.titles.at(k)<<" = "<<fixed<<setprecision(4)<<regional_results.outputs.at(k).front()<<" "<<regional_results.units.at(k)<<endl;

        copy_regional_results(regional_results, results, region, grid, regional_grid, i);

        //Write results from this region's process to disk so that
        //the main program can compile all regional results together
        //once all the processes are done.
        base = "region";
        sprintf(s,"_%05lld_plot_data",i+1);
        base.append(s);
        write_output(base, results, grid, plot_options, 0);//0 because this isn't the final write_output.

        //If desired, save output from all points in each region,
        //center and overlap. This produces a new folder with a new
        //output_filenames.txt, which has ALL parameters for ALL regs
        //(which have to be plotted separately).
        if(results.options.save_overlap_points==1){
          //overlap_results.options.copy_overlap_points==1, so
          //this statement copies overlap points too.
          copy_regional_results(regional_results, overlap_results, region, grid, regional_grid, i);

          //Append the title so the region is obvious for each plot.
          for(k=0;k<(long long)overlap_results.titles.size();k++){
            sprintf(s,", Rg %2lld",i+1);
            overlap_results.titles.at(k).append(s);
          }
          base = "region";
          sprintf(s,"_%05lld_overlap_data",i+1);
          base.append(s);
          write_output(base, overlap_results, grid, overlap_plot_options, 0);
        }//End of "save_overlap_points".

        exit(i);//Exit value of child process is the region index.
        break;
      }//End of child process commands.
    }//End of switch-case-break for "parent or child".
  }//End of loop through regions.

  //Wait for still-running procs (should be "numprocesses" of em..)
  //But if there are fewer regions (being analyzed!) than numprocs,
  //only wait for the number of regs that are really being analyzed.
  for(i=0;i<min((long long)results.options.numprocesses,(long long)region.indices_to_analyze.size());i++) wait(&rv);

  //Each of these processes has written its data to disk.  Search for
  //these files and load them into results.  Also record
  //the names of these temporary files in temp_options so they can
  //be easily deleted after the final results are written to disk.
  temp_options = plot_options;
  find_and_load_parallel_region_output(region, temp_options, results, grid);

  //Sanity check.
  if(results.options.type != -1 and ((long long)results.options.output_type.size() != (long long)results.outputs.size() or (long long)results.titles.size() != (long long)results.outputs.size())){
    cout<<"!!WARNING!!! results.options.output_type.size()="<<results.options.output_type.size()<<" while results.outputs.size()="<<results.outputs.size()<<" in gaia's parallel region loop after find_and_load_parallel_region_output."<<endl;
    for(i=0;i<(long long)results.titles.size();i++) cout<<"mult_titles["<<i<<"]= "<<results.titles.at(i)<<endl;
    for(i=0;i<(long long)results.options.output_type.size();i++) cout<<"output_type["<<i<<"]= "<<results.options.output_type.at(i)<<endl;
    for(i=0;i<(long long)results.outputs.size();i++) cout<<"mult_outputs["<<i<<"].size()= "<<results.outputs.at(i).size()<<endl;
  }
}

void write_supporting_grid(grid_s &grid){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;
  grid_s suppout_grid;
  results_s suppout_results;
  plot_options_s suppout_plot_options;
  string suppout_base;

  //Resize arrays, define title and output folder and filename base.
  suppout_results.outputs.resize(1);//Only a single output file.
  cout<<"ALSO PUT SUPPTYPE AND MULTS IN TITLE IN BOTH gaia AND sim_accels!"<<endl;
  suppout_results.titles.push_back("Supporting Grid Points");
  suppout_plot_options.outputfolder=gaiafolder();
  suppout_plot_options.outputfolder.append("output/temp/");
  suppout_base="support_grid";
  suppout_results.options.output_choice=3;

  //Save main and support grid points and type in mult_out (as double,
  //but write_output() type 3 will write them as long long).
  for(i=0;i<(long long)grid.lat.size();i++){
    suppout_grid.lat.push_back(grid.lat.at(i));
    suppout_grid.lon.push_back(grid.lon.at(i));
    //Main grid points are always type 0.
    suppout_results.outputs.front().push_back(0);
    for(j=0;j<(long long)grid.support_lat.at(i).size();j++){
      suppout_grid.lat.push_back(grid.support_lat.at(i).at(j));
      suppout_grid.lon.push_back(grid.support_lon.at(i).at(j));
      //Support gr pts have type that's based on their assoc main gr pt.
      suppout_results.outputs.front().push_back(i+1);
    }
  }
  write_output(suppout_base, suppout_results, suppout_grid, suppout_plot_options, 1);
}

void estimate_land_tide(results_s &results, grid_s &grid){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;

  results.error_bars.clear();
  //display_output(results, grid,50);
  convert_results_from_ampphase_to_sincos(results);
  for(j=0;j<(long long)results.options.output_tide.size();j++){
    if(results.options.output_tide.at(j)==1){
      if(results.options.output_type.at(j)==101){//101=cos,102=sin so j=co,j+1=sn
        long long ocean_pts=0, land_pts=0;
        double cocean_avg=0, cland_avg=0, cocean_dev=0, cland_dev=0;
        double cocean_max = -10000, cland_max = cocean_max;
        double socean_avg=0, sland_avg=0, socean_dev=0, sland_dev=0;
        double socean_max = cocean_max, sland_max = cocean_max;
        double land_area = 0.0, ocean_area = 0.0;
        for(i=0;i<(long long)grid.lat.size();i++){
          double weight = grid.area.at(i);
          if(grid.wcover[i] > 0.0){
            ocean_pts++;
            ocean_area += weight;
            cocean_avg += weight*results.outputs.at(j).at(i);
            cocean_dev += weight*pow(results.outputs.at(j).at(i),2);
            if(cocean_max < results.outputs.at(j).at(i)) cocean_max = results.outputs.at(j).at(i);
            socean_avg += weight*results.outputs.at(j+1).at(i);
            socean_dev += weight*pow(results.outputs.at(j+1).at(i),2);
            if(socean_max < results.outputs.at(j+1).at(i)) socean_max = results.outputs.at(j+1).at(i);
          }
          else{
            land_pts++;
            land_area += weight;
            cland_avg += weight*results.outputs.at(j).at(i);
            cland_dev += weight*pow(results.outputs.at(j).at(i),2);
            if(cland_max < results.outputs.at(j).at(i)) cland_max = results.outputs.at(j).at(i);
            sland_avg += weight*results.outputs.at(j+1).at(i);
            sland_dev += weight*pow(results.outputs.at(j+1).at(i),2);
            if(sland_max < results.outputs.at(j+1).at(i)) sland_max = results.outputs.at(j+1).at(i);
          }
        }
        cocean_avg /= ocean_area; cocean_dev /= ocean_area;
        cland_avg /= land_area; cland_dev /= land_area;
        socean_avg /= ocean_area; socean_dev /= ocean_area;
        sland_avg /= land_area; sland_dev /= land_area;

        if(1==1){
          cout<<endl<<"Parameter "<<results.titles.at(j)<<endl;
          cout<<"Ocean pts : "<<ocean_pts<<endl;
          cout<<"Land pts  : "<<land_pts<<endl;
          cout<<"cOcean max: "<<cocean_max<<endl;
          cout<<"cLand max : "<<cland_max<<endl;
          cout<<"cOcean avg: "<<cocean_avg<<endl;
          cout<<"cLand avg : "<<cland_avg<<endl;
          cout<<"cOcean dev: "<<cocean_dev<<endl;
          cout<<"cLand dev : "<<cland_dev<<endl<<endl;
          cout<<"sOcean max: "<<socean_max<<endl;
          cout<<"sLand max : "<<sland_max<<endl;
          cout<<"sOcean avg: "<<socean_avg<<endl;
          cout<<"sLand avg : "<<sland_avg<<endl;
          cout<<"sOcean dev: "<<socean_dev<<endl;
          cout<<"sLand dev : "<<sland_dev<<endl;
        }
        //2010-07-07 Correction: don't need to square the deviations again.
        double land_tide = sqrt(sland_dev+cland_dev);
        cout<<"Parameter "<<results.titles.at(j)<<" has area-weighted land  amplitude: "<<land_tide<<endl;
        results.error_bars.push_back(land_tide);
        //2010-07-07 Correction: don't need to square the deviations again.
        double ocean_tide = sqrt(socean_dev+cocean_dev);
        cout<<"Parameter "<<results.titles.at(j)<<" has area-weighted ocean amplitude: "<<ocean_tide<<endl;
      }
      else results.error_bars.push_back(-1);//All the tide sine coefs.
    }
    else results.error_bars.push_back(-1);//All the nontide params.
  }
  //If needed, convert all sin/cos output back to amp/phase
  if(results.conversion_to_sincos == 2) convert_results_from_sincos_to_ampphase(results);
}

void relative_reduction(results_s &results){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k;
  results_s temp = results;

  //Loop through all plots.
  for(i=0;i<(long long)results.xy.y_values.size();i++){
    //Subtract all lines from 1st line, report relative reduction %.
    for(j=1;j<(long long)results.xy.y_values.at(i).size();j++){
      //Loop through points in this line.
      for(k=0;k<(long long)results.xy.y_values.at(i).at(j).size();k++){
        //Calculate relative reduction %.
        results.xy.y_values.at(i).at(j).at(k) = 100.0*(results.xy.y_values.at(i).front().at(k) - results.xy.y_values.at(i).at(j).at(k))/results.xy.y_values.at(i).front().at(k);
      }
    }
    //Remove first line in this plot, which is that of the orig accels.
    results.xy.legends.at(i).erase(results.xy.legends.at(i).begin());
    results.xy.x_values.at(i).erase(results.xy.x_values.at(i).begin());
    results.xy.y_values.at(i).erase(results.xy.y_values.at(i).begin());
    //Alter y units.
    results.xy.y_units.at(i)+=" % reduction";
  }

  //Add a new plot containing orig spectra- inversion and verification.
  copy_plot_line(temp,results,1,1,1);//Orig plot #, line #, new plot in noise.
  results.xy.legends.back().back() = "Inversion";
  copy_plot_line(temp,results,2,1,0);//Orig plot #, line #, new plot in noise.
  results.xy.legends.back().back() = "Verification";
  results.xy.titles.back() = "Original Power Spectra";
}

void decimate_fes(latlon_s &rawfes, latlon_s &rawfes2, int decimate){
  /**********************************************************************
  Purpose:  This function accepts the rawfes struct and decimates it in 
            the lat/lon directions by "decimate" then stores result in
            rawfes2.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k,im,jm;
  rawfes2=rawfes;//An exact copy, because mask, minlat/lon remain identical. 
  im = rawfes.lon.size() / decimate; jm = rawfes.lat.size() / decimate;
  long long lonrem = rawfes.lon.size() % decimate; long long latrem = rawfes.lat.size() % decimate;

  //Now that I know im and jm, clear and resize fes vectors.
  rawfes2.amplitudes.clear();
  rawfes2.phases.clear();

  //Now define latitude and longitude vectors.
  rawfes2.lat.clear();
  rawfes2.lat.resize(jm);
  rawfes2.lon.clear();
  rawfes2.lon.resize(im);

  //1st dimension is latitude.
  rawfes2.amplitudes.resize(rawfes2.lat.size());
  rawfes2.phases.resize(rawfes2.lat.size());

  //2nd dimension is longitude.
  for(k=0;k<(long long)rawfes2.lat.size();k++){
    rawfes2.amplitudes.at(k).resize(rawfes2.lon.size(),0.0);
    rawfes2.phases.at(k).resize(rawfes2.lon.size(),0.0);
  }

  long long j2=-1,i2=-1;
  for(j=0;j<(long long)rawfes.lat.size()-latrem;j+=decimate){
    j2++;
    rawfes2.lat.at(j2) = rawfes.lat.at(j);
    i2=-1;
    for(i=0;i<(long long)rawfes.lon.size()-lonrem;i+=decimate){
      i2++;
      if(j==0) rawfes2.lon.at(i2) = rawfes.lon.at(i);
      rawfes2.amplitudes.at(j2).at(i2) = rawfes.amplitudes.at(j).at(i);
      rawfes2.phases.at(j2).at(i2) = rawfes.phases.at(j).at(i);
    }
  }
  cout<<"j2 = "<<j2<<", jm = "<<rawfes2.lat.size()<<", i2 = "<<i2<<", im = "<<rawfes2.lon.size()<<endl;
}

void subtract_fes(latlon_s &rawfes, latlon_s &rawfes2, int mode){
  /**********************************************************************
  Purpose:  This function calculates rawfes-rawfes2 using sin/cos, stores
            amps/phases in rawfes.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;
  double cos1,sin1,cos2,sin2,cos3=0.0,sin3=0.0;

  if(rawfes.lat.size() != rawfes2.lat.size() or rawfes.lon.size() != rawfes2.lon.size()) cout<<"WARNING!!! FES files are incompatible!"<<endl;

  for(j=0;j<(long long)rawfes.lat.size();j++){
    for(i=0;i<(long long)rawfes.lon.size();i++){
      //If BOTH FES maps have the mask value here, keep it as a mask value.
      if((rawfes.amplitudes.at(j).at(i) == rawfes.mask and rawfes.phases.at(j).at(i) == rawfes.mask) and (rawfes2.amplitudes.at(j).at(i)==rawfes2.mask and rawfes2.phases.at(j).at(i)==rawfes2.mask)){
        //Do nothing.
      } 
      else{
        //This allows FES truncation errors on land to be subtracted correctly.
        //If either FES map has the mask value here, set it to 0.0 first rather than ignoring it.
        if(rawfes.amplitudes.at(j).at(i) == rawfes.mask or rawfes.phases.at(j).at(i) == rawfes.mask){
          rawfes.amplitudes.at(j).at(i) = 0.0;
          rawfes.phases.at(j).at(i) = 0.0;
        }
        if(rawfes2.amplitudes.at(j).at(i)==rawfes2.mask or rawfes2.phases.at(j).at(i)==rawfes2.mask){
          rawfes2.amplitudes.at(j).at(i) = 0.0;
          rawfes2.phases.at(j).at(i) = 0.0;
        }
        cos1 = rawfes.amplitudes.at(j).at(i)*cos(rawfes.phases.at(j).at(i)*deg2rad);
        sin1 = rawfes.amplitudes.at(j).at(i)*sin(rawfes.phases.at(j).at(i)*deg2rad);
        cos2 = rawfes2.amplitudes.at(j).at(i)*cos(rawfes2.phases.at(j).at(i)*deg2rad);
        sin2 = rawfes2.amplitudes.at(j).at(i)*sin(rawfes2.phases.at(j).at(i)*deg2rad);
        if(mode==1){
          cos3 = cos1-cos2;
          sin3 = sin1-sin2;
        }
        else if(mode==0){
          cos3 = cos1+cos2;
          sin3 = sin1+sin2;
        }
        rawfes.amplitudes.at(j).at(i) = sqrt(pow(sin3,2)+pow(cos3,2));
        rawfes.phases.at(j).at(i) = restrict_degrees360(rad2deg*atan2(sin3,cos3));
      }
    }
  }
}

void parse_input_line(char s[max_length], long long &onetime, double &onelat, double &onelon, double &oneaccel, int &oneascending, double &onexA, double &onexB, double &oneyA, double &oneyB, double &onezA, double &onezB, int no_orbit_data){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  if(no_orbit_data == 0)
    sscanf(s,ORBIT_READ_FORMAT, &onetime, &onelat, &onelon, &oneaccel, &oneascending, &onexA, &onexB, &oneyA, &oneyB, &onezA, &onezB);
  else
    sscanf(s,ACCEL_READ_FORMAT, &onetime, &onelat, &onelon, &oneaccel, &oneascending);
}

void match_fes_to_grid(latlon_s &rawfes,grid_s &grid){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  int verbose=1;
  long long enough_warnings=50;//Otherwise 2nd pass prints too many warnings.
  long long i,j,k;
  long long missed_bins=0;//Keeps track of how many pts weren't binned.
  long long doubled_bins=0;//Keeps track of how many pts binned > once.
  grid.fes_index.clear();
  grid.fes_areas.clear();
  grid.fes_lat_indices.clear();
  grid.fes_lon_indices.clear();
  grid.fes_index.resize(rawfes.lat.size());
  for(j=0;j<(long long)rawfes.lat.size();j++) grid.fes_index.at(j).resize(rawfes.lon.size());

  //Use grid_cm2kg to calc area of FES pts at different lats.
  grid_s areagrid;
  for(j=0;j<(long long)rawfes.lat.size();j++){
    areagrid.lat.push_back(rawfes.lat.at(j));
    areagrid.different_lats.push_back(rawfes.lat.at(j));
    areagrid.lat_halfwidth.push_back(fabs(rawfes.lat.at(1) - rawfes.lat.at(0))/2.0);
  }
  areagrid.lon.resize(areagrid.lat.size(),0.0);//Why not?
  areagrid.options.type = 1;
  areagrid.options.area_type = 2;
  grid_cm2kg(areagrid);
  //Haven't yet accounted for multiple FES pts at each latitude.
  for(j=0;j<(long long)rawfes.lat.size();j++) grid.fes_areas.push_back(areagrid.area.at(j)/(double)rawfes.lon.size());
  
  grid.fes_lat_indices.resize(grid.lat.size());
  grid.fes_lon_indices.resize(grid.lat.size());
  //Loop through main grid points.
  for(k=0;k<(long long)grid.lat.size();k++){
    //Just so bin boundaries are defined. lat,lon indices NEED to be zero!
    int newbin=1; is_pt_in_bin(grid,rawfes.lat.front(),rawfes.lon.front(),k,newbin);
    //Loop through FES latitudes.
    for(j=0;j<(long long)rawfes.lat.size();j++){
      //Don't bother continuing unless this lat is in the bin's lat limits.
      if(rawfes.lat.at(j)<grid.maxlat and rawfes.lat.at(j)>=grid.minlat){
        //Loop through FES longitudes.
        for(i=0;i<(long long)rawfes.lon.size();i++){
          if(is_pt_in_bin(grid,rawfes.lat.at(j),rawfes.lon.at(i),k,newbin)){
            grid.fes_index.at(j).at(i).push_back(k);
          }//End of "if this FES pt is in the bin."
        }//End of loop through FES lons.
      }//End of "if this line of lat is in bin's lat limits."
    }//End of loop through FES lats.
  }//End of loop through main grid points.
  //Now that I've binned the "easy" points, it's time to catch the
  //few points that fall on the line between bins, so they're not
  //caught by "is_pt_in_bin".  For these, use spherical_dist().
  //Loop through FES latitudes.
  for(j=0;j<(long long)rawfes.lat.size();j++){
    //Loop through FES longitudes.
    for(i=0;i<(long long)rawfes.lon.size();i++){
      //Only examine FES points that are in the grid boundaries.
      if(is_pt_in_grid(grid,rawfes.lat.at(j),rawfes.lon.at(i)) == 1){
        //Finalize FES points that were binned exactly once.
        if(grid.fes_index.at(j).at(i).size() == 1){
          long long templl = grid.fes_index.at(j).at(i).front();
          grid.fes_lat_indices.at(templl).push_back(j);
          grid.fes_lon_indices.at(templl).push_back(i);
        }
        //Examine FES points that haven't been binned.
        else if(grid.fes_index.at(j).at(i).size() == 0){
          if(verbose and missed_bins < enough_warnings) cout<<"FES pt at "<<setw(10)<<fixed<<setprecision(4)<<rawfes.lat.at(j)<<", "<<rawfes.lon.at(i)<<" was chosen by "<<grid.fes_index.at(j).at(i).size()<<" bins using fast code. "<<endl;
          long long templl = grid_index_via_distance(grid, rawfes.lat.at(j),rawfes.lon.at(i), verbose and missed_bins < enough_warnings);
          grid.fes_index.at(j).at(i).push_back(templl);
          grid.fes_lat_indices.at(templl).push_back(j);
          grid.fes_lon_indices.at(templl).push_back(i);
          missed_bins++;
        }
        //Or, if this point was placed in >1 bin, find the closer one.
        else if(grid.fes_index.at(j).at(i).size() > 1){
          if(verbose and doubled_bins < enough_warnings) cout<<"FES pt at "<<setw(10)<<fixed<<setprecision(4)<<rawfes.lat.at(j)<<", "<<rawfes.lon.at(i)<<" was chosen by "<<grid.fes_index.at(j).at(i).size()<<" bins using fast code. "<<endl;
          long long templl = grid_index_via_distance(grid, rawfes.lat.at(j),rawfes.lon.at(i), verbose and doubled_bins < enough_warnings);
          if(verbose and doubled_bins < enough_warnings and count(grid.fes_index.at(j).at(i).begin(),grid.fes_index.at(j).at(i).end(),templl) != 1){
            cout<<"NOTE: Bins picked by fast code are at:"<<endl;
            for(long long temper=0;temper<(long long)grid.fes_index.at(j).at(i).size();temper++) cout<<"Lat: "<<grid.lat.at(grid.fes_index.at(j).at(i).at(temper))<<", "<<grid.lon.at(grid.fes_index.at(j).at(i).at(temper))<<endl;
          }
          grid.fes_index.at(j).at(i).clear();
          grid.fes_index.at(j).at(i).push_back(templl);
          grid.fes_lat_indices.at(templl).push_back(j);
          grid.fes_lon_indices.at(templl).push_back(i);
          doubled_bins++;
        }
      }
    }
  }
  cout<<"Number of FES points that weren't binned the first time: "<<missed_bins<<endl;
  cout<<"Number of FES points that were binned twice (or more) the first time: "<<doubled_bins<<endl;
}

void sum_fes_areas_in_each_mascon(grid_s &grid){
  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,k;
  //Loop through main grid points, average amps/phases in each bin.
  for(k=0;k<(long long)grid.lat.size();k++){

    //Loop through FES pts in this bin.
    double denom=0.0;
    if(grid.fes_lat_indices.at(k).size() == 0 or grid.fes_lon_indices.at(k).size() == 0) cout<<"WARNING!!! fes_lat/lon_indices are empty!!!"<<endl;
    if(grid.fes_lat_indices.at(k).size() != grid.fes_lon_indices.at(k).size()) cout<<"WARNING!!! fes_lat/lon_indices have different sizes!!!"<<endl;
    for(i=0;i<(long long)grid.fes_lat_indices.at(k).size();i++){
      long long tempj = grid.fes_lat_indices.at(k).at(i);
      denom += grid.fes_areas.at(tempj);
    }//End of loop through FES pts in this mascon.

    grid.area.at(k) = denom;
    //cout<<"Mascon area: "<<setw(11)<<scientific<<setprecision(4)<<grid.area.at(k)<<", FES area: "<<denom<<", FES/mascon: "<<denom/grid.area.at(k)<<endl;
  }//End of loop through main grid points.
}

void scale_plot(results_s &results,int target,int source){
  /**********************************************************************
  Purpose: Scale line at target index so it has the same max/min as the
          line in source index (-1 means all other lines are "source")
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j,k;
  int found_target=0,found_source=0;
  //Loop through separate plots.
  for(i=0;i<(long long)results.xy.y_values.size();i++){
    double sourcemax=-1000000,sourcemin=-sourcemax,targetmax=sourcemax,targetmin=-sourcemax;
    //Loop through lines in this plot.
    for(j=0;j<(long long)results.xy.y_values.at(i).size();j++){
      if(j==target){
        found_target=1;
        //Loop through points in this line.
        for(k=0;k<(long long)results.xy.y_values.at(i).at(j).size();k++){
          double value = results.xy.y_values.at(i).at(j).at(k);
          if(value > targetmax) targetmax = value;
          if(value < targetmin) targetmin = value;
        }
      }
      if(j!=target and (j==source or source == -1)){
        found_source=1;
        //Loop through points in this line.
        for(k=0;k<(long long)results.xy.y_values.at(i).at(j).size();k++){
          double value = results.xy.y_values.at(i).at(j).at(k);
          if(value > sourcemax) sourcemax = value;
          if(value < sourcemin) sourcemin = value;
        }
      }
    }
    //After loop through lines in this plot, target and source have both been seen, right?
    if(found_target == 0) cout<<"WARNING!!! Target was not found!"<<endl;
    if(found_source == 0) cout<<"WARNING!!! Source was not found!"<<endl;
    cout<<"targetmax = "<<scientific<<targetmax<<endl;
    cout<<"targetmin = "<<scientific<<targetmin<<endl;
    cout<<"sourcemax = "<<scientific<<sourcemax<<endl;
    cout<<"sourcemin = "<<scientific<<sourcemin<<endl;
    double a=(sourcemax-sourcemin)/(targetmax-targetmin),b=sourcemin-targetmin*a;
    cout<<"a = "<<scientific<<a<<endl;
    cout<<"b = "<<scientific<<b<<endl;
    j=target;//Loop through points in target line.
    for(k=0;k<(long long)results.xy.y_values.at(i).at(j).size();k++){
      double value = results.xy.y_values.at(i).at(j).at(k);
      results.xy.y_values.at(i).at(j).at(k) = value*a + b;
    }
  }
}

void combine_grids(grid_s &grid1,grid_s &grid2, int choice){
  /**********************************************************************
  Purpose:  This function combines grid1,2 as specified by choice, and
            stores the result in grid1.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long i;

  //Loop through grid points.
  for(i=0;i<(long)grid1.lat.size();i++){
    if(choice == 0){
      //New grid variables should be added here.
      grid1.lat.at(i) = grid1.lat.at(i) - grid2.lat.at(i);
      grid1.lon.at(i) = grid1.lon.at(i) - grid2.lon.at(i);
      grid1.cm2kg.at(i) = grid1.cm2kg.at(i) - grid2.cm2kg.at(i);
      grid1.lat_halfwidth.at(i) = grid1.lat_halfwidth.at(i) - grid2.lat_halfwidth.at(i);
      grid1.lon_halfwidth.at(i) = grid1.lon_halfwidth.at(i) - grid2.lon_halfwidth.at(i);
    }
    else if(choice == 10){ 
      //New grid variables should be added here.
      if(fabs(grid2.lat.at(i)) > tolerance) grid1.lat.at(i) = grid1.lat.at(i) / grid2.lat.at(i);
      else grid1.lat.at(i) = 0;
      
      if(fabs(grid2.lon.at(i)) > tolerance) grid1.lon.at(i) = grid1.lon.at(i) / grid2.lon.at(i);
      else grid1.lon.at(i) = 0;
      
      if(fabs(grid2.cm2kg.at(i)) > tolerance) grid1.cm2kg.at(i) = grid1.cm2kg.at(i) / grid2.cm2kg.at(i);
      else grid1.cm2kg.at(i) = 0;
      
      if(fabs(grid2.lat_halfwidth.at(i)) > tolerance) grid1.lat_halfwidth.at(i) = grid1.lat_halfwidth.at(i) / grid2.lat_halfwidth.at(i);
      else grid1.lat_halfwidth.at(i) = 0;
      
      if(fabs(grid2.lon_halfwidth.at(i)) > tolerance) grid1.lon_halfwidth.at(i) = grid1.lon_halfwidth.at(i) / grid2.lon_halfwidth.at(i);
      else grid1.lon_halfwidth.at(i) = 0;
      
    }
    else cout<<"!!!!WARNING!!! ERROR DETECTED!!!"<<endl;
  }
  return;
}

void xyz2llh(double x, double y, double z,
             double &lat, double &lon, double &h){
  /**********************************************************************
  Purpose: This function converts an XYZ coordinate in Earth-centered
          Earth-fixed frame to latitude, longitude, height
          using the WGS84 reference geoid.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          x,y,z     - Input XYZ coordinates in the ECEF frame (km).
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          lat,lon,h - Output lat, lon (degrees) and height (km).
  ***********************************************************************
  Originally written by Stefan Roettger as minicrs::ECEF2LLH()
  in minicrs.cpp in libmini, licensed under the LGPL and hosted here:
  http://code.google.com/p/libmini/
  
  For future upgrades, see here:
  "A number of techniques and algorithms are available but the most accurate according to Zhu[2], is the following 15 step procedure summarised by Kaplan."
  http://en.wikipedia.org/wiki/Geodetic_system#geodetic_to.2Ffrom_ECEF_coordinates
  J. Zhu, "Conversion of Earth-centered Earth-fixed coordinates to geodetic coordinates," Aerospace and Electronic Systems, IEEE Transactions on, vol. 30, pp. 957–961, 1994.
  **********************************************************************/
  double sth,cth,slat,clat; // sine and cosine values
  double p,th;              // temporary variables
  double r;                 // radius in prime vertical

  p=sqrt(x*x+y*y);
  th=atan(z*GEOID_r_major/(p*GEOID_r_minor));

  sth=sin(th); cth=cos(th);

  //transformed latitude
  lat=atan((z+GEOID_ed2*GEOID_r_minor*sth*sth*sth)/(p-GEOID_e2*GEOID_r_major*cth*cth*cth));

  //transformed longitude
  lon=atan2(y,x);

  slat=sin(lat); clat=cos(lat);

  r=GEOID_r_major/sqrt(1.0-GEOID_e2*slat*slat);

  //transformed height
  if (clat>0.0) h=p/clat-r;
  else h=r;

  lat*=rad2deg; lon*=rad2deg;
}

void llh2xyz(double &x, double &y, double &z,
             double lat, double lon, double h){
  /**********************************************************************
  Purpose: This function converts a coordinate in latitude, longitude, 
          height coordinate to XYZ format in an Earth-centered
          Earth-fixed frame using the WGS84 reference geoid.
          NOTE: Variables are in xyzllh order rather than llhxyz
                so that xyz2llh() and llh2xyz() have the same args.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
          lat,lon,h - Input lat, lon (degrees) and height (km).
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          x,y,z     - Output XYZ coordinates in the ECEF frame (km).
  ***********************************************************************
  Originally written by Stefan Roettger as minicrs::LLH2ECEF()
  in minicrs.cpp in libmini, licensed under the LGPL and hosted here:
  http://code.google.com/p/libmini/
  **********************************************************************/
  double slat,clat,slon,clon; // sine and cosine values
  double r;                   // radius in prime vertical

  lat*=deg2rad; lon*=deg2rad;

  slat=sin(lat); clat=cos(lat);
  slon=sin(lon); clon=cos(lon);

  r=GEOID_r_major/sqrt(1.0-GEOID_e2*slat*slat);

  x=(r+h)*clat*clon;
  y=(r+h)*clat*slon;
  z=(r*(1.0-GEOID_e2)+h)*slat;
}

void load_arbitrary_time_series(input_s &input, results_s &results){
  /**********************************************************************
  Purpose: If any arbitrary parameter types are requested, load the 
           arbitrary time series and interpolate them to the input times.
           Save the interpolated time series to disk in cache/input.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i;
  //Used to push_back vectors of dbl vectors
  vector<double> temp_v_double;
  //push_back vs of vs of dbl vectors
  vector< vector<double> > temp_vv_double;
  //push_back vs of string vectors
  vector<string> temp_v_string;

  //I don't like to make redundant copies, but this is very convenient.
  input.arbitrary.parameter_type = results.options.parameter_type;
  input.arbitrary.parameter = results.options.parameter;

  //Search for requested arbitrary parameters, load them from disk.
  for(i=0;i<(long long)results.options.parameter_type.size();i++){
    if(results.options.parameter_type.at(i)>=10000 and results.options.parameter_type.at(i)<20000){
      input.arbitrary.current_parameter = i;
      read_arbitrary_file(input.arbitrary,results.options.parameter_type.at(i));
    }
    else{
      //For easy comparison with parameter_type vector, even
      //non-arbitrary parameters have entries in this structure.
      input.arbitrary.titles.push_back("NOT ARBITRARY"); 
      input.arbitrary.x_units.push_back("NA");
      input.arbitrary.y_units.push_back("NA");

      //This is a brand new plot, so push_back using temp vectors/vvs.
      input.arbitrary.x_values.push_back(temp_vv_double);
      input.arbitrary.y_values.push_back(temp_vv_double);
      input.arbitrary.legends.push_back(temp_v_string);
      //Each of these "NOT ARBITRARY" plots will have 0 lines.
    }
  }
}

void read_arbitrary_file(xy_s &arbitrary, long long type){
  /**********************************************************************
  Purpose: This function reads an arbitrary time series file. 
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,time_long;
  double temp_double;
  string file, line1;
  //Custom time conversion functions require this structure as a container.
  time_conversion_s time_conversion; time_conversion.initialized=0;
  time_conversion.calendar_time.resize(6,0);
  ifstream input_file;//Used to grab strings from the file.
  istringstream stream1;//Used to grab data from the strings.

  //Used to push_back vectors of dbl vectors
  vector<double> temp_v_double;
  //push_back vs of vs of dbl vectors
  vector< vector<double> > temp_vv_double;
  //push_back vs of string vectors
  vector<string> temp_v_string;

  //Cecilia's time is in "days of 2005" where 12:0:0 jan 1, 2005 is 1.5:
  time_conversion.calendar_time.front() = 2004;//year
  time_conversion.calendar_time.at(1) = 12;//month - 0 is meaningless!
  time_conversion.calendar_time.at(2) = 31;//day - 0 is meaningless!
  time_conversion.calendar_time.at(3) = 0;//hours
  time_conversion.calendar_time.at(4) = 0;//minutes
  time_conversion.calendar_time.at(5) = 0;//seconds
  cal2utc(time_conversion);//Convert calendar time to UTC seconds.
  long long cecilia_utc_epoch = time_conversion.utc_seconds;

  double omega=0.0;

  if(type == 11001 or type == 10001 or type == 10101 or type == 10102){
    if(type == 11001){
      arbitrary.titles.push_back("Daily Arctic OBP, highpass filtered"); 
      arbitrary.x_units.push_back("seconds");
      arbitrary.y_units.push_back("scale");
    }
    else if(type == 10001){
      arbitrary.titles.push_back("Arbitrary secular trend"); 
      arbitrary.x_units.push_back("seconds");
      arbitrary.y_units.push_back("cm/yr");
    }
    else if(type == 10101){
      arbitrary.titles.push_back("Arbitrary cosine term"); 
      arbitrary.x_units.push_back("seconds");
      arbitrary.y_units.push_back("cm");
      omega = twoPi/arbitrary.parameter.at(arbitrary.current_parameter);
    }
    else if(type == 10102){
      arbitrary.titles.push_back("Arbitrary sine term"); 
      arbitrary.x_units.push_back("seconds");
      arbitrary.y_units.push_back("cm");
      omega = twoPi/arbitrary.parameter.at(arbitrary.current_parameter);
    }
    else{
      arbitrary.titles.push_back("Arbitrary type not recognized"); 
      arbitrary.x_units.push_back("NA");
      arbitrary.y_units.push_back("NA");
    }

    //This is a brand new plot, so push_back using temp vectors/vvs.
    arbitrary.x_values.push_back(temp_vv_double);
    arbitrary.y_values.push_back(temp_vv_double);
    arbitrary.legends.push_back(temp_v_string);

    //There's also just 1 dataseries per plot, so push_back now.
    arbitrary.x_values.back().push_back(temp_v_double);
    arbitrary.y_values.back().push_back(temp_v_double);
    
    file = gaiafolder();
    file.append("data/arbitrary_time_series/");
    file.append("HF_Arctic_OBP.txt");
    //Open file for reading.
    input_file.open(file.c_str());
    //Warn if file doesn't open correctly.
    if(input_file == NULL) cout<<"The arbitrary time series file, "<< file << ", failed to open."<<endl;
    //Read each line until the file ends.
    while(getline(input_file,line1)){
      //Convert "calendar time" at noon to secs past J2000.
      stream1.clear();
      stream1.str(line1);
      stream1 >> time_long;
      //cout<<"year: "<<time_long<<endl;
      time_conversion.calendar_time.front() = time_long;
      stream1 >> time_long;
      //cout<<"month: "<<time_long<<endl;
      time_conversion.calendar_time.at(1) = time_long;
      stream1 >> time_long;
      //cout<<"day: "<<time_long<<endl;
      time_conversion.calendar_time.at(2) = time_long;
      time_conversion.calendar_time.at(3) = 12;//hours.
      time_conversion.calendar_time.at(4) = 0;//minutes.
      time_conversion.calendar_time.at(5) = 0;//seconds.
      cal2utc(time_conversion);//Convert calendar time to UTC seconds.
      //Record UTC seconds as "x" in xy struct.
      arbitrary.x_values.back().at(0).push_back(time_conversion.utc_seconds);
      
      stream1 >> temp_double;//Read bottom pressure in cm water equiv.
      //cout<<"bp: "<<temp_double<<endl;
      //Record cm water as "y" in xy struct.
      arbitrary.y_values.back().at(0).push_back(temp_double);
    }
    input_file.close();//Close the current file.
    if(type == 10001){//Replace with secular trend in cm/yr.
      //Exactly 1 cm/yr.
      double slope = inverse_seconds_in_year;
      //So that the time series is zero at the center of the orig times.
      double start = -0.5*slope*(arbitrary.x_values.back().at(0).back() - arbitrary.x_values.back().at(0).front());
      for(i=0;i<(long long)arbitrary.y_values.back().at(0).size();i++){
        arbitrary.y_values.back().at(0).at(i) = start+slope*(arbitrary.x_values.back().at(0).at(i) - arbitrary.x_values.back().at(0).front());
      }
    }
    else if(type == 10101){
      for(i=0;i<(long long)arbitrary.y_values.back().at(0).size();i++){
        arbitrary.y_values.back().at(0).at(i) = cos(omega*arbitrary.x_values.back().at(0).at(i));
      }
    }
    else if(type == 10102){
      for(i=0;i<(long long)arbitrary.y_values.back().at(0).size();i++){
        arbitrary.y_values.back().at(0).at(i) = sin(omega*arbitrary.x_values.back().at(0).at(i));
      }
    }
  }
  else if(type == 10201){
    arbitrary.titles.push_back("S&P 500 Daily Closing 2002-2010"); 
    arbitrary.x_units.push_back("seconds");
    arbitrary.y_units.push_back("scale");

    //This is a brand new plot, so push_back using temp vectors/vvs.
    arbitrary.x_values.push_back(temp_vv_double);
    arbitrary.y_values.push_back(temp_vv_double);
    arbitrary.legends.push_back(temp_v_string);

    //There's also just 1 dataseries per plot, so push_back now.
    arbitrary.x_values.back().push_back(temp_v_double);
    arbitrary.y_values.back().push_back(temp_v_double);
    
    file = gaiafolder();
    file.append("data/arbitrary_time_series/");
    file.append("sp500_daily_closing_2002-2010.txt");
    //Open file for reading.
    input_file.open(file.c_str());
    //Warn if file doesn't open correctly.
    if(input_file == NULL) cout<<"The arbitrary time series file, "<< file << ", failed to open."<<endl;
    //Read each line until the file ends.
    while(getline(input_file,line1)){
      //Convert "calendar time" at noon to secs past J2000.
      stream1.clear();
      stream1.str(line1);
      stream1 >> time_long;
      //cout<<"year: "<<time_long<<endl;
      time_conversion.calendar_time.front() = time_long;
      stream1 >> time_long;
      //cout<<"month: "<<time_long<<endl;
      time_conversion.calendar_time.at(1) = time_long;
      stream1 >> time_long;
      //cout<<"day: "<<time_long<<endl;
      time_conversion.calendar_time.at(2) = time_long;
      time_conversion.calendar_time.at(3) = 12;//hours.
      time_conversion.calendar_time.at(4) = 0;//minutes.
      time_conversion.calendar_time.at(5) = 0;//seconds.
      cal2utc(time_conversion);//Convert calendar time to UTC seconds.
      //Record UTC seconds as "x" in xy struct.
      //cout<<"UTC: "<<time_conversion.utc_seconds<<endl;
      arbitrary.x_values.back().at(0).push_back(time_conversion.utc_seconds);
      
      stream1 >> temp_double;
      arbitrary.y_values.back().at(0).push_back(temp_double);
    }
    input_file.close();//Close the current file.
  }
  else if(type == 11002 or type == 11003){
    if(type == 11002){
      arbitrary.titles.push_back("ABPR1 hourly, de-trended"); 
    }
    else if(type == 11003){
      arbitrary.titles.push_back("ABPR1 hourly, de-trended, de-tided"); 
    }
    arbitrary.x_units.push_back("seconds");
    arbitrary.y_units.push_back("scale");

    //This is a brand new plot, so push_back using temp vectors/vvs.
    arbitrary.x_values.push_back(temp_vv_double);
    arbitrary.y_values.push_back(temp_vv_double);
    arbitrary.legends.push_back(temp_v_string);

    //There's also just 1 dataseries per plot, so push_back now.
    arbitrary.x_values.back().push_back(temp_v_double);
    arbitrary.y_values.back().push_back(temp_v_double);
    
    file = gaiafolder();
    file.append("data/arbitrary_time_series/");
    file.append("ABPR1_hourly_4Bryan012411.txt");
    //Open file for reading.
    input_file.open(file.c_str());
    //Warn if file doesn't open correctly.
    if(input_file == NULL) cout<<"The arbitrary time series file, "<< file << ", failed to open."<<endl;
    long long bad_numbers=0;//Keep track of how many NaN/etc are discarded.
    //Read each line until the file ends.
    while(getline(input_file,line1)){
      //Convert "calendar time" at noon to secs past J2000.
      stream1.clear();
      stream1.str(line1);
      stream1 >> time_long;
      //cout<<"year: "<<time_long<<endl;
      time_conversion.calendar_time.front() = time_long;
      stream1 >> time_long;
      //cout<<"month: "<<time_long<<endl;
      time_conversion.calendar_time.at(1) = time_long;
      stream1 >> time_long;
      //cout<<"day: "<<time_long<<endl;
      time_conversion.calendar_time.at(2) = time_long;
      stream1 >> time_long;
      //cout<<"hour: "<<time_long<<endl;
      time_conversion.calendar_time.at(3) = time_long;//hours.
      stream1 >> time_long;
      //cout<<"minute: "<<time_long<<endl;
      time_conversion.calendar_time.at(4) = 0;//minutes.
      stream1 >> time_long;
      //cout<<"second: "<<time_long<<endl;
      time_conversion.calendar_time.at(5) = 0;//seconds.
      cal2utc(time_conversion);//Convert calendar time to UTC seconds.
      //cout<<"UTC: "<<time_conversion.utc_seconds<<endl;
      
      if(type == 11002){//Use the first value.
        if((stream1 >> temp_double).fail()){
          bad_numbers++;
          //cout<<"NaN discarded."<<endl;
        }
        else{
          //cout<<"obp in cm: "<<temp_double<<endl;
          arbitrary.x_values.back().at(0).push_back(time_conversion.utc_seconds);
          arbitrary.y_values.back().at(0).push_back(temp_double);
        }
      }
      else if(type == 11003){//Ignore the first value, use the second.
        stream1 >> temp_double;
        if((stream1 >> temp_double).fail()){
          bad_numbers++;
          //cout<<"NaN discarded."<<endl;
        }
        else{
          //cout<<"obp in cm: "<<temp_double<<endl;
          arbitrary.x_values.back().at(0).push_back(time_conversion.utc_seconds);
          arbitrary.y_values.back().at(0).push_back(temp_double);
        }
      }
    }
    if(bad_numbers > 0) cout<<"Discarded "<<bad_numbers<<" NaN's."<<endl;
    input_file.close();//Close the current file.
  }
  else if(type == 11004){
    arbitrary.titles.push_back("ABPR1 15min, de-trended"); 
    arbitrary.x_units.push_back("seconds");
    arbitrary.y_units.push_back("scale");

    //This is a brand new plot, so push_back using temp vectors/vvs.
    arbitrary.x_values.push_back(temp_vv_double);
    arbitrary.y_values.push_back(temp_vv_double);
    arbitrary.legends.push_back(temp_v_string);

    //There's also just 1 dataseries per plot, so push_back now.
    arbitrary.x_values.back().push_back(temp_v_double);
    arbitrary.y_values.back().push_back(temp_v_double);
    
    file = gaiafolder();
    file.append("data/arbitrary_time_series/");
    file.append("ABPR1_every15minData_4Bryan012411.txt");
    //Open file for reading.
    input_file.open(file.c_str());
    //Warn if file doesn't open correctly.
    if(input_file == NULL) cout<<"The arbitrary time series file, "<< file << ", failed to open."<<endl;
    
    long long bad_numbers=0;//Keep track of how many NaN/etc are discarded.
    //Read each line until the file ends.
    while(getline(input_file,line1)){
      stream1.clear();
      stream1.str(line1);
      stream1 >> temp_double;
      temp_double *= 86400;//Convert "days of 2005" to seconds.
      //Now convert from seconds since 0:0:0 Dec 31,2004.
      time_conversion.utc_seconds = cecilia_utc_epoch + (long long)temp_double;
      //cout<<"UTC: "<<time_conversion.utc_seconds<<endl;
      
      //cout<<line1<<endl;
      if((stream1 >> temp_double).fail()){
        bad_numbers++;
        //cout<<"NaN discarded."<<endl;
      }
      else{
        //cout<<"obp in cm: "<<temp_double<<endl;
        arbitrary.x_values.back().at(0).push_back(time_conversion.utc_seconds);
        arbitrary.y_values.back().at(0).push_back(temp_double);
      }
    }
    if(bad_numbers > 0) cout<<"Discarded "<<bad_numbers<<" NaN's."<<endl;
    input_file.close();//Close the current file.
  }
  else if(type == 11101){
    arbitrary.titles.push_back("Upernavik monthly GRACE de-trended"); 
    arbitrary.x_units.push_back("seconds");
    arbitrary.y_units.push_back("scale");

    //This is a brand new plot, so push_back using temp vectors/vvs.
    arbitrary.x_values.push_back(temp_vv_double);
    arbitrary.y_values.push_back(temp_vv_double);
    arbitrary.legends.push_back(temp_v_string);

    //There's also just 1 dataseries per plot, so push_back now.
    arbitrary.x_values.back().push_back(temp_v_double);
    arbitrary.y_values.back().push_back(temp_v_double);
    
    file = gaiafolder();
    file.append("data/arbitrary_time_series/");
    file.append("upernavik.detrended_values.tex");
    //Open file for reading.
    input_file.open(file.c_str());
    //Warn if file doesn't open correctly.
    if(input_file == NULL) cout<<"The arbitrary time series file, "<< file << ", failed to open."<<endl;
    
    long long bad_numbers=0;//Keep track of how many NaN/etc are discarded.
    //Read each line until the file ends.
    while(getline(input_file,line1)){
      stream1.clear();
      stream1.str(line1);
      stream1 >> temp_double;
      time_conversion.dyr = temp_double;
      dyr2utc(time_conversion);//Convert decimal years to UTC seconds.
      //cout<<"UTC: "<<time_conversion.utc_seconds<<endl;
      
      //cout<<line1<<endl;
      if((stream1 >> temp_double).fail()){
        bad_numbers++;
        //cout<<"NaN discarded."<<endl;
      }
      else{
        //cout<<"obp in cm: "<<temp_double<<endl;
        arbitrary.x_values.back().at(0).push_back(time_conversion.utc_seconds);
        arbitrary.y_values.back().at(0).push_back(temp_double);
      }
    }
    if(bad_numbers > 0) cout<<"Discarded "<<bad_numbers<<" NaN's."<<endl;
    input_file.close();//Close the current file.
  }
  else if(type == 12001){
    insitu_s insitu;
    ancillary_s ancillary;
    grid_s grid;
    model_s model;
    insitu_initialize(insitu, grid, model.options, ancillary);
    load_insitu(insitu,ancillary);

    //Copy from insitu to arbitrary.

  }
  else cout<<"WARNING!!! Don't recognize type "<<type<<endl;
}

void arbitrary_interpolate(input_s &input, grid_s &region){
  /**********************************************************************
  Purpose: Interpolate arbitrary time series to the input times.
           If region code was used for loading, save the interpolated time
           series to disk in cache/input.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;

  //If region code wasn't used to load input, the input time series is
  //already in memory.
  if((long long)region.lat.size() == 0){
    cout<<"WARNING!!! Non-region code in arbitrary_interpolate hasn't been written yet!"<<endl;
  }

  //If region code was used to load input, the input time series needs
  //to be loaded from disk.
  if((long long)region.lat.size() != 0){
    input_s temp_input,empty_input;
    grid_s temp_grid;
    grid_s temp_region;//lat will be empty, so this loads all input.
    temp_input.options = input.options;
    load_input(temp_input,temp_grid,temp_region);
    input.time = temp_input.time;
    temp_input = empty_input;//To clear all the data from memory.
    results_s temp_arb;
    temp_arb.xy = input.arbitrary;
    interpolate_preloaded_input_and_arbitrary(input);
    if(1==2){//Save xy plots with original and interpolated on same plot.
      //Used to push_back vectors of dbl vectors
      vector<double> temp_v_double;
      for(i=0;i<(long long)input.arbitrary.y_values.size();i++){
        //Not arbitrary unless this plot has at least 1 line.
        if((long long)input.arbitrary.y_values.at(i).size()>0){
          temp_arb.xy.x_values.at(i).push_back(temp_v_double);
          temp_arb.xy.y_values.at(i).push_back(temp_v_double);
          temp_arb.xy.legends.at(i).push_back("original");
          temp_arb.xy.legends.at(i).push_back("interpolated");
          for(j=0;j<(long long)input.arbitrary.y_values.at(i).at(0).size();j++){
            //Don't bother saving the zero values.
            if(input.arbitrary.y_values.at(i).at(0).at(j) != 0.0 and temp_arb.xy.x_values.at(i).back().size() < 40000){
              temp_arb.xy.x_values.at(i).back().push_back(input.time.at(j)+input.epoch);
              temp_arb.xy.y_values.at(i).back().push_back(input.arbitrary.y_values.at(i).at(0).at(j));
            }
          }
          //Erase orig arb values outside the trimmed interpolated values.
          j=0;
          while(j<(long long)temp_arb.xy.x_values.at(i).at(0).size()){
            //cout<<"size: "<<temp_arb.xy.x_values.at(i).at(0).size()<<endl;
            //Don't bother saving the zero values.
            if(temp_arb.xy.x_values.at(i).at(0).at(j) < temp_arb.xy.x_values.at(i).at(1).front() or temp_arb.xy.x_values.at(i).at(0).at(j) > temp_arb.xy.x_values.at(i).at(1).back()){
              //cout<<"deleting index: "<<j<<endl;
              temp_arb.xy.x_values.at(i).at(0).erase(temp_arb.xy.x_values.at(i).at(0).begin()+j);
              temp_arb.xy.y_values.at(i).at(0).erase(temp_arb.xy.y_values.at(i).at(0).begin()+j);
            }
            else j++;
          }
        }
      }
      //Before saving to disk, remove empty non-arbitrary placeholders.
      i=0;
      while(i<(long long)temp_arb.xy.y_values.size()){
        //Delete if this plot has no lines.
        if((long long)temp_arb.xy.y_values.at(i).size()==0){
          temp_arb.xy.x_values.erase(temp_arb.xy.x_values.begin()+i);
          temp_arb.xy.y_values.erase(temp_arb.xy.y_values.begin()+i);
          temp_arb.xy.titles.erase(temp_arb.xy.titles.begin()+i);
          temp_arb.xy.legends.erase(temp_arb.xy.legends.begin()+i);
          temp_arb.xy.x_units.erase(temp_arb.xy.x_units.begin()+i);
          temp_arb.xy.y_units.erase(temp_arb.xy.y_units.begin()+i);
        }
        else i++;
      }
      //Save to disk.
      plot_options_s temp_options2;
      temp_arb.options.output_choice = 2;//2D plots.
      temp_options2.outputfolder = gaiafolder();
      temp_options2.outputfolder.append("output/temp/");
      write_output("interpolation", temp_arb, temp_grid, temp_options2,1);
      cout<<"now crashing."<<endl;
      cout<<temp_arb.xy.titles.at(1000000);
    }
    save_interpolated_arbitrary_to_cache(input);
    //This isn't needed anymore.
    input.time.clear();
    //Just to make sure.
    input.arbitrary.closest_indices.clear();
    input.arbitrary.differences.clear();
    //Clear arbitrary by looping through list of arbitrary time series.
    for(i=0;i<(long long)input.arbitrary.y_values.size();i++){
      //Not arbitrary unless this plot has at least 1 line.
      if((long long)input.arbitrary.y_values.at(i).size()>0){
        //These are written to disk so they're not necessary anymore.
        input.arbitrary.x_values.at(i).at(0).clear();
        input.arbitrary.y_values.at(i).at(0).clear();
      }
    }
  }
}

void interpolate_preloaded_input_and_arbitrary(input_s &input){
  /**********************************************************************
  Purpose: Interpolate arbitrary time series to the input times.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;
  //Stores interpolated arbitrary time series until copied
  //to input.arbitrary at the end of this function.
  xy_s interp = input.arbitrary;

  //Loop through list of arbitrary time series.
  for(i=0;i<(long long)input.arbitrary.y_values.size();i++){
    //Not arbitrary unless this plot has at least 1 line.
    if((long long)input.arbitrary.y_values.at(i).size()>0){
      //Clear x_values in permanent version because they're redundant.
      interp.x_values.at(i).at(0).clear();
      //All y_values should be the same length as input time series now.
      interp.y_values.at(i).at(0).clear();
      //Any y_values outside of the orig y_values are 0.0.
      interp.y_values.at(i).at(0).resize(input.time.size(),0.0);
      //Just to make sure interpolation starts with a blank slate.
      input.arbitrary.closest_indices.clear();
      input.arbitrary.differences.clear();
      for(j=0;j<(long long)input.time.size();j++){
        //Don't include input times on starting boundary in case this function
        //is sent multiple sequential arbitrary time series: for instance, to
        //replace the orbit-accels combination program.
        long long input_time = input.time.at(j)+input.epoch;
        if(input_time > input.arbitrary.x_values.at(i).at(0).front() and input_time <= input.arbitrary.x_values.at(i).at(0).back()){
          closest_arbitrary_times(input, i, j);
          //cout<<"Input index "<<j<<", max/min diffs: "<<input.arbitrary.max_diff<<"/"<<input.arbitrary.min_diff<<", lg/sm ind: "<<input.arbitrary.largest_index<<"/"<<input.arbitrary.smallest_index<<endl;
          //cout<<"Input index "<<j<<", 0/1 diffs: "<<input.arbitrary.differences[0]<<"/"<<input.arbitrary.differences[1]<<", 0/1 ind: "<<input.arbitrary.closest_indices[0]<<"/"<<input.arbitrary.closest_indices[1]<<endl;
          //Use closest_indices to interpolate input.arbitrary values to input times.
          double time_factor = (input_time-input.arbitrary.x_values.at(i).at(0).at(input.arbitrary.closest_indices.at(1)))/(input.arbitrary.x_values.at(i).at(0).at(input.arbitrary.closest_indices.at(1))-input.arbitrary.x_values.at(i).at(0).at(input.arbitrary.closest_indices.at(0)));
          interp.y_values.at(i).at(0).at(j) = (input.arbitrary.y_values.at(i).at(0).at(input.arbitrary.closest_indices.at(1))-input.arbitrary.y_values.at(i).at(0).at(input.arbitrary.closest_indices.at(0)))*time_factor + input.arbitrary.y_values.at(i).at(0).at(input.arbitrary.closest_indices.at(1));
        }
      }
      //Print first and last nonzero values in this arbitrary series of y_values.
      long long nonzero1=-1,nonzero2=-1;
      for(j=0;j<(long long)interp.y_values.at(i).at(0).size();j++){
        if(interp.y_values.at(i).at(0).at(j) != 0.0){
          nonzero2 = j;//Record "last" nonzero value regardless.
          if(nonzero1 == -1) nonzero1 = j;//Only if this is the FIRST nonzero value.
        }
      }
      cout<<"After interpolating "<<input.arbitrary.titles.at(i)<<endl;
      if(nonzero1 != -1) cout<<"First nonzero arbitrary value at index "<<nonzero1<<" (UTC "<<input.time.at(nonzero1)+input.epoch<<") is "<<interp.y_values.at(i).at(0).at(nonzero1)<<endl;
      else cout<<"No nonzero values at all!"<<endl;
      if(nonzero2 != -1) cout<<"Last nonzero arbitrary value at index "<<nonzero2<<" (UTC "<<input.time.at(nonzero2)+input.epoch<<") is "<<interp.y_values.at(i).at(0).at(nonzero2)<<endl;
    }
  }
  //Copy interpolated version over original.
  input.arbitrary = interp;
}

void closest_arbitrary_times(input_s &input, long long i, long long j){
  /**********************************************************************
  Purpose: Find closest timestamp in arbitrary time series "i" 
           to input time "j". If closest_indices has size num_indices,
           assume time "j-1" was just used in the same manner.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long k;
  double temp_double;
  long long temp_long;
  input.arbitrary.numindices = 2;//Number of closest matching indices.
  if(input.arbitrary.closest_indices.size() != input.arbitrary.differences.size()) cout<<"WARNING!!! input.arbitrary.closest_indices.size() = "<<input.arbitrary.closest_indices.size()<<" while input.arbitrary.differences.size() = "<<input.arbitrary.differences.size()<<endl;
  
  //Most common case- assume time "j-1" was matched previously.
  if(input.arbitrary.closest_indices.size() == input.arbitrary.numindices){
    //Find new differences between closest matches to the last input pt.
    for(k=0;k<(long long)input.arbitrary.closest_indices.size();k++){
      input.arbitrary.differences.at(k) = input.arbitrary.x_values.at(i).at(0).at(input.arbitrary.closest_indices.at(k)) - input.time.at(j) - input.epoch;
    }
    //Do these matches need to be re-ordered?
    for(k=0;k<(long long)input.arbitrary.closest_indices.size()-1;k++){
      if(fabs(input.arbitrary.differences.at(k)) > fabs(input.arbitrary.differences.at(k+1))){
        temp_double = input.arbitrary.differences.at(k);
        temp_long = input.arbitrary.closest_indices.at(k);
        input.arbitrary.differences.at(k)       = input.arbitrary.differences.at(k+1);
        input.arbitrary.closest_indices.at(k)   = input.arbitrary.closest_indices.at(k+1);
        input.arbitrary.differences.at(k+1)     = temp_double;
        input.arbitrary.closest_indices.at(k+1) = temp_long;
      }
    }

    input.arbitrary.max_diff = *max_element(input.arbitrary.differences.begin(),input.arbitrary.differences.end());
    input.arbitrary.min_diff = *min_element(input.arbitrary.differences.begin(),input.arbitrary.differences.end());
    input.arbitrary.largest_index  = *max_element(input.arbitrary.closest_indices.begin(),input.arbitrary.closest_indices.end());
    input.arbitrary.smallest_index = *min_element(input.arbitrary.closest_indices.begin(),input.arbitrary.closest_indices.end());

    //If max difference is non-positive, arbitrary times are all BEFORE input time,
    //so try next index in list (unless it's the last index).
    if(input.arbitrary.max_diff <= 0 and input.arbitrary.largest_index < (long long)input.arbitrary.x_values.at(i).at(0).size()-1){
      while(is_this_arbitrary_point_closer(input,i,j,input.arbitrary.largest_index+1) and input.arbitrary.largest_index < (long long)input.arbitrary.x_values.at(i).at(0).size()-1){ }
    }
    //Else if min difference is non-negative, arbitrary times are all AFTER input time,
    //so try index before earliest index in list (unless the earliest index in list
    //is the first arbitrary pt.
    else if(input.arbitrary.min_diff >= 0 and input.arbitrary.smallest_index > 0){
      while(is_this_arbitrary_point_closer(input,i,j,input.arbitrary.smallest_index-1)){ }
    }
  }
  //If closest_indices is cleared, start at the beginning of arbitrary time series.
  else if(input.arbitrary.closest_indices.size() == 0){
    //Start at the beginning of the arbitrary time series.
    for(k=0;k<input.arbitrary.numindices;k++){
      if(is_this_arbitrary_point_closer(input,i,j,k)){ }
    }
    //If max difference is non-positive, arbitrary times are all BEFORE input time,
    //so try next index in list (unless it's the last index).
    if(input.arbitrary.max_diff <= 0 and input.arbitrary.largest_index < (long long)input.arbitrary.x_values.at(i).at(0).size()-1){
      while(is_this_arbitrary_point_closer(input,i,j,input.arbitrary.largest_index+1) and input.arbitrary.largest_index < (long long)input.arbitrary.x_values.at(i).at(0).size()-1){ }
    }
  }
  else{
    cout<<"WARNING!!! input.arbitrary.closest_indices.size() = "<<input.arbitrary.closest_indices.size()<<endl;
    cout<<"WARNING!!! input.arbitrary.differences.size() = "<<input.arbitrary.differences.size()<<endl;
  }
}

int is_this_arbitrary_point_closer(input_s &input, long long i,
                                   long long j, long long k){
  /**********************************************************************
  Purpose: Is arbitrary pt "k" in arbitrary time series "i" closer
           to input time "j" than any pts recorded in closest_indices?
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
          Returns "1" if it is closer, "0" if not. Also records 
          closest index and difference in arbitrary vectors.
  **********************************************************************/
  if(input.arbitrary.closest_indices.size() != input.arbitrary.differences.size()) cout<<"WARNING!!! input.arbitrary.closest_indices.size() = "<<input.arbitrary.closest_indices.size()<<" while input.arbitrary.differences.size() = "<<input.arbitrary.differences.size()<<endl;
  if(input.arbitrary.numindices == 0) cout<<"WARNING!!! input.arbitrary.numindices = "<<input.arbitrary.numindices<<endl;

  long long l;
  double diff = input.arbitrary.x_values.at(i).at(0).at(k) - input.time.at(j) - input.epoch;
  int closer = 0;//Assume this pt ISN'T closer until proven wrong.
  
  //Most common case- already found numindices closest matches.
  if(input.arbitrary.closest_indices.size() == input.arbitrary.numindices){
    l=0;//Compare to closest match first, then next-closest, etc.
    while(l<(long long)input.arbitrary.closest_indices.size() and closer == 0){
      if(fabs(diff) < fabs(input.arbitrary.differences.at(l))){
        input.arbitrary.closest_indices.insert(input.arbitrary.closest_indices.begin()+l,k);
        input.arbitrary.differences.insert(input.arbitrary.differences.begin()+l,diff);
        //Now delete farthest match to keep arbitrary.numindices matches only.
        input.arbitrary.closest_indices.pop_back();
        input.arbitrary.differences.pop_back();
        //Exit while loop.
        closer = 1;
      }
      else l++;//Compare to next-closest match.
    }
  }
  else if(input.arbitrary.closest_indices.size() == 0){//Start from scratch.
    input.arbitrary.closest_indices.push_back(k);
    input.arbitrary.differences.push_back(diff);
    closer = 1;//When closest_indices is cleared, this is true by default.
  }
  else if(input.arbitrary.closest_indices.size() < input.arbitrary.numindices){
    l=0;//Compare to closest match first, then next-closest, etc.
    while(l<(long long)input.arbitrary.closest_indices.size() and closer == 0){
      if(fabs(diff) < fabs(input.arbitrary.differences.at(l))){
        input.arbitrary.closest_indices.insert(input.arbitrary.closest_indices.begin()+l,k);
        input.arbitrary.differences.insert(input.arbitrary.differences.begin()+l,diff);
        //Exit while loop.
        closer = 1;
      }
      else l++;//Compare to next-closest match.
    }
    if(closer == 0){//Match isn't closer, but closest_indices isn't full.
      input.arbitrary.closest_indices.push_back(k);
      input.arbitrary.differences.push_back(diff);
      closer = 1;//Match found- albeit only to fill closest_indices.
    }
  }
  else{
    cout<<"WARNING!!! input.arbitrary.closest_indices.size() = "<<input.arbitrary.closest_indices.size()<<endl;
    cout<<"WARNING!!! input.arbitrary.differences.size() = "<<input.arbitrary.differences.size()<<endl;
  }
  input.arbitrary.max_diff = *max_element(input.arbitrary.differences.begin(),input.arbitrary.differences.end());
  input.arbitrary.min_diff = *min_element(input.arbitrary.differences.begin(),input.arbitrary.differences.end());
  input.arbitrary.largest_index  = *max_element(input.arbitrary.closest_indices.begin(),input.arbitrary.closest_indices.end());
  input.arbitrary.smallest_index = *min_element(input.arbitrary.closest_indices.begin(),input.arbitrary.closest_indices.end());
  return closer;
}

void save_interpolated_arbitrary_to_cache(input_s &input){
  /**********************************************************************
  Purpose: Save interpolated arbitrary time series to disk.
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
  long long i,j;
  FILE *new_fp;
  string filename;
  string newfile = input.arbitrary.cachefolder;
  filename=create_arbitrary_cache_filename(input);
  newfile.append(filename);
  cout<<"Writing interpolated arbitrary time series to cache file: "<<newfile<<endl;
  new_fp = fopen(newfile.c_str(),"w");
  for(i=0,j=0;i<(long long)input.arbitrary.y_values.size();i++){
    if((long long)input.arbitrary.y_values.at(i).size()>0){
      j++;
    }
  }
  fprintf(new_fp,"%lld\n",j);//number of arb params
  fprintf(new_fp,"%20s\n","Time");
  //Loop through all time values, identical for all interpolated arbitrary points.
  for(i=0;i<(long long)input.time.size();i++){
    fprintf(new_fp,"%20lld",input.time.at(i));//number of arb params
    //Loop through all arbitrary parameters for each line in cache file.
    for(j=0;j<(long long)input.arbitrary.y_values.size();j++){
      if((long long)input.arbitrary.y_values.at(j).size()>0){
        fprintf(new_fp,"%20.12e",input.arbitrary.y_values.at(j).at(0).at(i));
      }
    }
    fprintf(new_fp,"\n");
  }
  fclose(new_fp);

}

  /**********************************************************************
  Purpose: This function
  ***********************************************************************
  Input:  (types ending in "_s" are defined in definitions.hpp)
  ***********************************************************************
  Output: (types ending in "_s" are defined in definitions.hpp)
  **********************************************************************/
